Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
Upcoming SlideShare
Loading in...5
×
 

Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?

on

  • 975 views

Leo Antoli

Leo Antoli

Statistics

Views

Total Views
975
Views on SlideShare
975
Embed Views
0

Actions

Likes
0
Downloads
12
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala? Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala? Presentation Transcript

  • Quiero hacer ágil, ¿ y ahora qué: Java, Ruby o Scala ? Leo Antoli @lantoli Conferencia Agile-Spain 2011 - Castellón
  • Antes de empezar
      • Hacer ágil o ser ágil, esa es la cuestión
      • ¿ Se puede Cobol ágil o Ruby sin tests y en cascada ?
      • ¿ Se puede ser ágil con Java ?
      •   El lenguaje es muy importante... pero es sólo una herramienta
      • ¿ Por qué Java, Scala y Ruby ?
      • ¿ Cuáles son las buenas prácticas técnicas (aplicables a ágil y cascada ? 
      • ¿ Se pueden usar nuevos lenguajes en tus desarrollos actuales ?
  • Fuera de la sesión
      • Lenguajes en cliente
      • Frameworks
      • Sólo se tratan algunos lenguajes
  • Un lenguaje para dominarlos a todos
  • Popularidad
    • 1 - Java
    • 2 - C
    • 3 - C++
    • 4 - C#
    • 5 - PHP
    • 6 - Objective-C
    • 7 - Visual Basic
    • 8 - Python
    • 9 - Perl
    10 - Javascript 11 - Ruby 25 - COBOL 27 - Scheme (LISP) 30 - Fortran 35 - Haskell 40 - Prolog 50 - Scala >51 Groovy
  • Popularidad
    • Google Code Jam 2011
  • Causa/efecto o correlación
  • Sopa de letras
  • Diseño simple, arquitectura
    • - Pasa todas las pruebas  (hace lo que tiene que hacer, pero no más) - Minimiza las duplicaciones  (si quiero cambiar el comportamiento lo hago en un solo sitio) - Maximiza la claridad (expresa bien las intenciones, es fácil de entender) - Es conciso (usa el menor número de clases y métodos, cumpliendo las otras reglas)
    • Estos mandamientos se resumen en dos:
    • - Quitar duplicación de código
    • - Mejorar los nombres mal puestos
    • Arquitectura: El conjunto de decisiones de diseño significativas (costosas de cambiar)
  • Entrega continua de valor: ¿ sólo con pruebas automáticas ?
  • ¿ TDD/BDD obligatorio ? ... Anda antes de correr
  • Dinámico o Estático
    • - Estáticos tienen problemas, muchas redundancias y son menos legibles que los dinámicos
    • - La solución son los dinámicos o hay algo entre medias ?
  • Conciso o verboso
    • ...
    • public class StringCalculator {
    • public int add ( String inputStr ) {
    •     String firstLine = getLineDelimiters ( inputStr );
    •     String textWithNumbers = getStringWithoutDelimiterLine ( inputStr );
    •     List < String > delimiters = getDelimiters ( firstLine );
    •     List < Integer > numbers = getAllowedNumbers ( getNumbers ( textWithNumbers, delimiters ));
    •     checkNegativeNumbers ( numbers );
    •     return getSum ( numbers );
    • }
    • ...
    • private static int getSum ( List < Integer > numbers ) {     SUMA LOS ELEMENTOS DE UNA LISTA
    •     int sum = 0 ;
    •     for ( int num : numbers ) {
    •       sum += num ;
    •     }
    •     return sum ;
    • }
    • private static void checkNegativeNumbers ( List < Integer > numbers)  throws IllegalArgumentException {
    •     List < Integer > negatives = new ArrayList < Integer >(); DECLARAR VARIABLE
    •     for ( int num : numbers ) { LISTA NUMEROS NEGATIVOS
    •         if ( num < 0 ) {
    •               negatives . add ( num );
    •         }
    •     }
    •     if ( negatives . size () > 0 ) {
    •             throw new IllegalArgumentException ( &quot;no se permiten negativos: &quot;   + negatives . toString ());
    •     }
    • }
    • ...
    121 líneas
  • Conciso o verboso
    • class Integer
    •    def negative?
    •      self < 0
    •    end
    •    def suitable_for_string_calculator?
    •      self <= 1000
    •    end
    •    end
    • 
    • class Calculator
    •    def add (args)
    •      strnumbers, delimiter = extract_strnumbers_and_delimiter args
    •      numbers = get_number_list strnumbers, delimiter
    •      get_only_suitable_numbers numbers
    •      check_negatives_numbers numbers
    •      numbers . inject 0 , :+                                                                                  SUMA LOS ELEMENTOS DE UNA LISTA
    •    end
    •    def get_number_list (numbers_str, delimiter)
    •      numbers_str . split(delimiter) . collect { | num | num . to_i }
    •    end
    •    def get_only_suitable_numbers (numbers)
    •      numbers . select! & :suitable_for_string_calculator?
    •    end
    •    def check_negatives_numbers (numbers)
    •      negatives = numbers . select & :negative?                                                             LISTA NUMEROS NEGATIVOS
    •      raise &quot;negatives not allowed (#{ negatives . join( ', ' ) })&quot; if negatives . any?                  IF AL FINAL
    •    end
    •    def extract_strnumbers_and_delimiter (args)
    •        delimiters = /[n,]/
    •        text = args . dup
    •        first_line = text . slice!( %r{^//(.+)n} )
    •        delimiters = first_line . scan( %r{[([^]]+)]} ) . flatten << delimiters if first_line
    •        return text, Regexp . union(delimiters)
    •    end
    • end
    43 líneas
  • Conciso o verboso
    • ...
    • @Test
    • public void testAddEmpty () throws Exception {
    •     assertEquals ( &quot;adding empty string&quot; , 0 , calc . add ( &quot;&quot; ));
    • }
    • @Test
    • public void testSingleElement () throws Exception {
    •     assertEquals ( &quot;adding simple element&quot; , 1 , calc . add ( &quot;1&quot; ));
    •     assertEquals ( &quot;adding more simple elements&quot; , 345 , calc . add ( &quot;345&quot; ));
    • }
    • @Test
    • public void testTwoElementsSum () throws Exception {
    •     assertEquals ( &quot;adding two elements&quot; , 3 , calc . add ( &quot;1,2&quot; ));
    •     assertEquals ( &quot;adding two more elements&quot; , 201 , calc . add ( &quot;123,78&quot; ));
    • }
    • @Test
    • public void testNewLineDelimitier () throws Exception {
    •     assertEquals ( &quot;adding with different delimiter&quot; , 6 , calc . add ( &quot;1n2,3&quot; ));
    • }
    • PROBANDO EXCEPCIONES
    • @Test
    • public void testNegativesThrowsException () throws Exception {
    •     try {
    •       calc . add ( &quot;6,-8,3,-52&quot; );
    •     fail ( &quot;testing negative numbers, shouldn't be here&quot; );
    •     } catch ( Exception e ) {
    •       String msg = e . getMessage ();
    •       assertTrue ( &quot;contains negative sentence&quot; ,
    •       msg . contains ( &quot;no se permiten negativos&quot; ));
    •       assertTrue ( &quot;contains negative number&quot; , msg . contains ( &quot;-8&quot; ));
    •       assertTrue ( &quot;contains negative number&quot; , msg . contains ( &quot;-52&quot; ));
    •     }
    • }
    • ...
  • Conciso o verboso
    • describe &quot;String calculator&quot; do
    •    before do
    •      @calculator = Calculator . new
    •    end
    •    it &quot;empty should be 0&quot; do
    •      @calculator . add( &quot;&quot; ) . should == 0
    •    end
    • PROBANDO VARIOS CASOS CON HASHTABLE
    •    { &quot;&quot; => 0 , &quot;1&quot; => 1 , &quot;345&quot; => 345 , &quot;1,1&quot; => 2 , &quot;3,4&quot; => 7 , &quot;1,1,1&quot; => 3 ,
    •      &quot;1,2,3&quot; => 6 , &quot;5n2n3&quot; => 10 , &quot;123,78&quot; => 201 } . each do | numbers, result |
    •      it &quot;adding #{ numbers } should be #{ result }&quot; do
    •        @calculator . add(numbers) . should == result
    •      end
    •    end
    •    it &quot;delimiter , and n should work&quot; do
    •      @calculator . add( &quot;1n2,3&quot; ) . should == 6
    •    end
    •    it &quot;different delimiters specified in first line should work&quot; do
    •      @calculator . add( &quot;//[%]n2%6&quot; ) . should == 8
    •      @calculator . add( &quot;//[;]n1;2&quot; ) . should == 3
    •      @calculator . add( &quot;//[+]n8+12,43&quot; ) . should == 63
    •    end
    •    it &quot;doesn't allow negative numbers&quot; do PROBANDO EXCEPCIONES
    •       expect { @calculator . add( &quot;1n-2n-3n4&quot; ) } . to raise_error( Exception , &quot;negatives not allowed (-2, -3)&quot; )
    •     end
    •    it &quot;big numbers should be ignored&quot; do
    •      @calculator . add( &quot;2,1001&quot; ) . should == 2
    •      @calculator . add( &quot;2,1000&quot; ) . should == 1002
    •    end
    • ...
  • Conciso o verboso
    • ...
    • ROMANS = { M: 1000 , CM : 900 , D: 500 , CD : 400 , C: 100 , XC : 90 , L: 50 , XL : 40 , X: 10 , IX : 9 , V: 5 , IV : 4 , I: 1 }
    • class Fixnum
    •    def to_roman
    •      return nil unless self > 0 && self < 4000
    •      remaining_number = self
    •      ROMANS . inject ( &quot;&quot; ) do | roman_str, current_number |
    •          times,remaining_number = remaining_number . divmod current_number [ 1 ]
    •          roman_str + current_number [ 0 ]. to_s * times
    •      end
    •    end
    • end
    • TRANSFORMATIONS = {
    •    I: 1 , II : 2 , III : 3 , IV : 4 , V: 5 , VI : 6 , VII : 7 , VIII : 8 , IX : 9 , X: 10 , XI : 11 , XII : 12 , XIV : 14 , XV : 15 ,
    •    XIX : 19 , XXXIX : 39 , XL : 40 , XLI : 41 , L: 50 , LXXXIX : 89 , XC : 90 , XCIX : 99 , C: 100 , CCCXCIX : 399 , CD : 400 ,
    •    D: 500 , DCCCXCIX : 899 , CM : 900 , M: 1000 , MMXI : 2011 , MMMCMXCIX : 3999
    • }
    • describe &quot;From arabic to roman numerals. &quot; do
    •    TRANSFORMATIONS . each do | roman, arabic |
    •      it( &quot;transforms #{ arabic } to #{ roman }&quot; ) do
    •        arabic . to_roman . should == roman . to_s
    •      end
    •    end
    •    [ - 10 , 0 , 4000 , 4100 ]. each do | bad_arabic |
    •      it( &quot;#{ bad_arabic } can not be transformed to roman numeral&quot; ) do
    •        bad_arabic . to_roman . should == nil
    •      end
    •    end
    • end
  • Llamar métodos por nombre / introspección
    • class Integer
    •    def to_fizzbuzz1
    •      return &quot;FizzBuzz&quot; if fizzbuzz?
    •      return &quot;Fizz&quot; if fizz?
    •      return &quot;Buzz&quot; if buzz?
    •      self
    •    end
    •    FIZZBUZZ3 = { fizzbuzz?: &quot;FizzBuzz&quot; , buzz?: &quot;Buzz&quot; , fizz?: &quot;Fizz&quot; }
    •    def to_fizzbuzz3
    •      FIZZBUZZ3 .each do | method , name |
    •        return name if send method
    •      end
    •      self
    •    end
    •    def multiple_of? n
    •      self % n == 0
    •    end
    •    def fizz?
    •      multiple_of? 3
    •    end
    •   ...
    •    def fizzbuzz?
    •      multiple_of? 15
    •    end
  • Llamar métodos por nombre / introspección
    • describe &quot;FizzBuzz identity cases&quot; do
    •    it &quot;1 should be 1&quot; do
    •      1 . to_fizzbuzz . should == 1
    •    end
    •    it &quot;4 should be 4&quot; do
    •      4 . to_fizzbuzz . should == 4
    •    end
    • end
    • ...
    • describe &quot;FizzBuzz FizzBuzz cases&quot; do
    •      it &quot;15 should be Buzz&quot; do
    •        15 . to_fizzbuzz . should == &quot;FizzBuzz&quot;
    •      end
    • ...
    • end
    • describe &quot;FizzBuzz testing all implementations&quot; do
    •     [ :to_fizzbuzz1 , :to_fizzbuzz2 , :to_fizzbuzz3 ]. each do | impl |
    •         ( 1 . . 100 ) . each do | n |
    •           it &quot;trying impl. #{ impl } for number #{ n }&quot; do
    •             n . send(impl) . should == n . to_fizzbuzz
    •           end
    •         end
    •      end
    • end
  • Inferencia de tipos
    • Java
    • int x = 1 + 2 * 3;
    • String y = x.toString();
    • List<String> lista = new ArrayList<String>();
    • Map<String, Integer> m = new HashMap<String, Integer>();
    • public int incrementar(int x) {
    • return x + 1;
    • }
    • Java 7: 
    • Map<String, List<String>> myMap = new HashMap<>();
    • Scala
    • val x = 1 + 2 * 3 
    • val y = x.toString() 
    • val lista : List[String] = new ArrayList[String]
    • val lista = new ArrayList[String]
    • val lista = List
    • val lista = List(&quot;elm1&quot;, &quot;elm2&quot;)
    • val m = new HashMap[String,Int]
    • def incrementar(x: Int) : Int = x + 1
    • def incrementar(x: Int) = x + 1
    •  
  • Tipos covariantes y contracovariantes, not reified (type erasure), ...
    • Java:
    • Vehiculo[] v = new Vehiculo[10];
    • Coche[] c = new Coche[10];
    • v = c; // CORRECTO
    • List<Vehiculo> vlist = new ArrayList<Vehiculo>();
    • List<Coche> vcoche = new ArrayList<Coche>();
    • vlist = vcoche; 
    • // ERROR - Type mismatch: cannot convert from List<Coche> to List<Vehiculo>
    • Scala:
    • class Stack[+A] {
    •     def push[B >: A](elem: B) ....
  • Abrir clases o conversiones implícitas (vistas)
    • 2 days ago 
    • 5 days from_now
    • class DateHelper(number: Int) { 
    •   def days(when: String) : Date = { 
    •     var date = Calendar.getInstance() 
    •     when match { 
    •         case &quot;ago&quot; => date.add(Calendar.DAY_OF_MONTH, -number) 
    •         case &quot;from_now&quot; => date.add(Calendar.DAY_OF_MONTH, number) 
    •         case _ => date 
    •     } 
    •     date.getTime() 
    •   } 
    • }
    • implicit def convertInt2DateHelper(number: Int) = new DateHelper(number)
    • final class RichChar(c: Char)
    •   { 
    •           def isDigit: Boolean = Character.isDigit(c) // isLetter, isWhitespace, etc.  
    •   } 
    •   object RichCharTest {
    •          implicit def charWrapper(c: char) = new RichChar(c) 
    •          def main(args: Array[String]) { println( '0' .isDigit)
    •   }
    • }
  • Duck typing, Interfaces o Structural typing
    • class Duck {  
    •   def quack = println ( &quot;Quaaaaaack !&quot; )  
    •    def feathers = println ( &quot;The duck has white and gray feathers.&quot; )  
    • }   
    • class Person {  
    •   def quack = println ( &quot;The person imitates a duck.&quot; )  
    •   def feathers = println ( &quot;The person takes a feather from the ground and shows it.&quot; )  
    • }
    • ...  
    • def inTheForest ( duck : { def quack ; def feathers }) = {  
    •     duck.quack 
    •     duck.feathers 
    • } type ActAsADuck = {
    •     def quack 
    •     def feathers
    • }
  • Missing method, invokedynamic (java7), Dynamic trait
    • class Roman
    •   def romanToInt(str)
    •     # ...
    •   end
    •   def method_missing(methId)
    •     str = methId.id2name
    •     romanToInt(str)
    •   end
    • end
    • r = Roman.new
    • r.iv      #=> 4
    • r.xxiii   #=> 23
    • r.mm      #=> 2000
    • miXml.persona.nombre , o miJson.persona.nombre ;-)
    • Rails lo usa muchísimo, por ejemplo find_by_columns
  • Módulos, mixins, traits, herencia múltiple, ...
    • class miclase
    • {
    •     include Enumerable # tengo gratis inject, all?, any?, collect...
    •     def each ....
    • }
    • Comparable, con <=> tengo <, <=, ==, >, >=, between?
    • Puedo mezclar los módulos que quiera
  • JVM
    • Groovy
    • Scala
    • JRuby
    • Jython
    • Clojure
    • ...
    • Quien lo iba a imaginar
  • Rendimiento
    • ¿ Compilado o interpretado ?
    • ¿ Estáticos o dinámicos ? 
    • ¿ Intensivo IO o CPU ?
    • ¿ Funcionales (y no-SQL), objectivo contrario a lenguajes dinámicos ?
    • Actors ? transactional memory ? &quot;let it crash&quot; ? Monads ?
    &quot;SETI@home has been a success,obviously not in finding aliens,but in demonstrating the potential of large-scale distributed computing&quot;
  • Funcionales, recursión, complejidad, O(N), ...
    •    def to_fib_recursive
    •      return self if zero? or one?
    •      ( self - 1 ) . to_fib_recursive + ( self - 2 ) . to_fib_recursive
    •    end
    •    def to_fib_tail
    •    return self if zero? or one?
    •      first,second = 0 , 1
    •      ( self - 1 ) . times do
    •        first,second = second, first + second
    •      end
    •      second
    •    end
    •    def to_fib_inject
    •      return self if zero? or one?
    •      fib = ( self - 1 ) . times . inject( [ 0 , 1 ] ) do | group, n |
    •         [ group [ 1 ] , group [ 0 ] + group [ 1 ] ]
    •      end
    •      fib [ 1 ]
    •    end
    •    def to_fib_formula
    •      ( ( ( GOLDEN_RATIO ** self ) - (( - GOLDEN_RATIO ) ** ( - self )) ) / ROOT_FIVE ) . to_i
    •    end
    •    def one?
    •        self == 1
    •    end
    •    ROOT_FIVE = Math . sqrt( 5 )
    •    GOLDEN_RATIO = ( 1 + ROOT_FIVE ) / 2
    • end
  • No olvidemos...
      • IDE y herramientas
      • Rendimiento
      • Chequeos compilación
      • Independencia plataforma
        • Lenguaje
        • Librerías
      • Muy bueno para frameworks genéricos (Rails, RSpec, Cucumber, etc.) pero para APIs ni interfaces ni tipos
      • Los dinámicos son de verdad más productivos ?
  • ¡WARNING! La siguiente sección contiene opiniones subversivas que pueden herir la sensibilidad
  • Tipos de tipados  :-)
    • ¿Se realiza chequeo de tipos?
      • Sí -> Tipado fuerte
      • No -> Tipado débil
    • ¿Cuándo se realiza el chequeo de tipos?
      • En tiempo de ejecución -> Tipado dinámico
      • En tiempo de compilación -> Tipado estático
  • Tipos de tipados  :-)
    • ¿Java, Scala, C#...? Tipado fuerte y estático
    • ¿JS, Ruby ....? Tipado fuerte y dinámico
    • ¿C, C++? Tipado débil (al menos en mis tiempos)
  • El compilador es tonto
      • Necesita información extra para chequear los tipos
        • Ruido sintáctico -> Boilerplate code
        • Dependencia del IDE (CTRL+Space)
        • Reza para que sea rápido (ADA, ¿Scala?)
      • Los errores de tipos son los sencillos
      • Es incapaz de detectar los errores semánticos, los realmente complicados
  • Enter TDD (que cansino...)
      • No existe técnica para resolver el problema de detección de errores funcionales al 100%
      • Pero TDD y BDD son muy efectivos!
  • Enter TDD (que cansino...)
      • No existe técnica para resolver el problema de detección de errores funcionales al 100%
      • Pero TDD y BDD son muy efectivos!
    • Ahora os cuento un secreto....
  • Enter TDD (que cansino...)
      • No existe técnica para resolver el problema de detección de errores funcionales al 100%
      • Pero TDD y BDD son muy efectivos!
    • Ahora os cuento un secreto....
    • ¡ TDD también detecta errores sintácticos y de tipos !
    • ¡ Y los detecta al 100 % !
    • (al menos en mi experiencia)
  • Tipado dinámico wins!
    • Invocando el principio KISS:
    • IF ( &quot;TDD lo necesito sí o sí&quot; 
    •       && &quot;TDD detecta errores de tipado y de sintaxis&quot;) {
    •     DO Eliminar compilador
    • }
    • Y de paso elimino el ruido sintáctico:
      • Escribo menos código
      • El código es más legible
      • Ciclo de desarrollo más ágil
  • Tipado estático strikes back
      • Inferencia de tipos
      • Tipado estructural
    • ¿Tanta complejidad hará que el compilador sea lento?
    • ¿Realmente que me aporta?
    • ¿Qué gano respecto a un lenguaje dinámico usando TDD?
  • ¿Y los DSL?
      • Hacen tu código más cercano al dominio del problema
      • Legibilidad aumenta
      • Lenguajes dinámicos son buenos para esto
        • Son maleables
        • Muchas capacidades de metaprogramación
        • ¡Ruby y Groovy!
      • Los lenguajes estáticos normalmente son malos para esto: JMock Vs. Spock Vs. RSpec Vs. Jasmine
      • ! Pero Scala es muy bueno !
  • ¡YA ME VOY! Y ahora pasemos a las conclusiones (Gracias Leo!)
  • Conclusiones
    • - ¿ Dinámicos para equipos pequeños con gente muy buena ?
    • - ¿  Estáticos  para proyectos grandes o con varios equipos ?
    • - ¿  Funcionales  para problemas específicos ?
    • Kent Beck:  &quot;Simplemente cuenta la historia.  No me gustan las conclusiones morales al final de las historias.&quot;
    • Así que cada uno saque sus propias conclusiones :-)
    • Muchas gracias
    Leo Antoli               @lantoli Enrique Amodeo     @eamodeorubio