• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala?
 

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

on

  • 925 views

Leo Antoli

Leo Antoli

Statistics

Views

Total Views
925
Views on SlideShare
925
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