SlideShare una empresa de Scribd logo
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;1\n2,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;5\n2\n3&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;1\n2,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;1\n-2\n-3\n4&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

Más contenido relacionado

PPTX
Módulo 4 (sin estilo)
DOCX
Instrucciones(raptor, java, c#)
PPT
Capitulo 8 Cadenas
PPT
Linq
PPTX
Funciones str
DOCX
Lista de Comandos
PPT
Caracteres y Cadenas en C
PPT
CARACTERES Y CADENAS LENGUAJE C
Módulo 4 (sin estilo)
Instrucciones(raptor, java, c#)
Capitulo 8 Cadenas
Linq
Funciones str
Lista de Comandos
Caracteres y Cadenas en C
CARACTERES Y CADENAS LENGUAJE C

La actualidad más candente (20)

ODP
Fundamentos de programación en scilab
DOCX
Comandos
PDF
PDF
Encriptacion y crackmes_en_batch_desde_0_by_xassiz
PPT
DOCX
Comandos de Raptor,C# y Java
PPSX
Cadenas De Caracteres
ODP
Fundamentos de computación(ICM) en octave
PDF
Analisis sintactico10
PDF
Ejercicios punteros cadenas-vectores
PDF
Java argumentos variables
PPT
Operaciones Basicas C++
PPTX
PDF
08 strings o cadenas
PDF
Solucion ejercicios punteros cadenas-vectores
PDF
Clase 5
PPTX
Los lenguajes
PPTX
Funciones en Lenguaje C
PDF
Programación 1: cadenas en C
PPT
Programacion
Fundamentos de programación en scilab
Comandos
Encriptacion y crackmes_en_batch_desde_0_by_xassiz
Comandos de Raptor,C# y Java
Cadenas De Caracteres
Fundamentos de computación(ICM) en octave
Analisis sintactico10
Ejercicios punteros cadenas-vectores
Java argumentos variables
Operaciones Basicas C++
08 strings o cadenas
Solucion ejercicios punteros cadenas-vectores
Clase 5
Los lenguajes
Funciones en Lenguaje C
Programación 1: cadenas en C
Programacion
Publicidad

Similar a Quiero hacer ágil, ¿y ahora qué: Java, Ruby o Scala? (20)

PPT
Curso de Macros Excel
PPT
El lenguaje c
PPT
Tipos de datos en C
PPT
Clase lenguaje c
PPT
Clase lenguaje c xxxxxx
PPT
Clase lenguaje c xxxxxx
PPT
Linq 1207579553462901 8
PDF
Idiomatic Ruby
PPT
Presentación1
PPT
Presentación1
PPT
Presentación1
PPT
Dce0 programacion oo_c#_vbnet
PPT
Dce0 programacion oo_c#_vbnet
PPT
Programacion C#_vbnet
PPT
Programacion c# vbnet
PPT
Elementos básicos de c++
PPT
Elementos básicos de c++
PPT
Lenguaje c++
PPT
Lenguaje c++
Curso de Macros Excel
El lenguaje c
Tipos de datos en C
Clase lenguaje c
Clase lenguaje c xxxxxx
Clase lenguaje c xxxxxx
Linq 1207579553462901 8
Idiomatic Ruby
Presentación1
Presentación1
Presentación1
Dce0 programacion oo_c#_vbnet
Dce0 programacion oo_c#_vbnet
Programacion C#_vbnet
Programacion c# vbnet
Elementos básicos de c++
Elementos básicos de c++
Lenguaje c++
Lenguaje c++
Publicidad

Más de Agile Spain (20)

PDF
Lessons learned from contrasting Design Thinking and Agile Project Management...
PDF
Visual Scrum - What you see is What you get
PDF
Un Primer Paso a la Agilidad: Retrospectivas para el Aprendizaje de la Ingeni...
PDF
Análisis de la implementación de prácticas ágiles en Argentina
PDF
Como cocinar tu contrato ágil
ODP
Introducción a la agilidad
PDF
Cas2010 desarrollo-de-aplicaciones-en-la-nube-con-scrum-y-xp
PDF
Cas2010 gestion-agil-de-la-configuracion
PDF
Cas2010 itinerario-implementacion-agil
PDF
Cas2010 gestion-agil-de-equipos
PDF
Cas2010 integrando-practicas-agiles-y-de-experiencia-de-usuario
PDF
Cas2010 toolchain-for-agile-teams-traceability-from-product-vision-to-working...
PDF
Cas2010 los-principios-agiles-como-guia-o-por-que-querras-volver-a-modelos-tr...
PDF
Cas2010 to-track-defects-or-not-to-track-defects-that-is-the-question
PDF
Cas2010 is-there-space-for-testers-in-agile-projects
PDF
Cas2010 one-year-of-software-developments-to-win-a-world-racing-championship
PDF
Cas2010 pair-programming-strategies
PDF
Cas2010 behavior-driven-development-aplicado-en-acceptance-test-automation
PDF
Cas2010 herramientas-de-pruebas-unitarias-pex-y-moles
PDF
Ser ágil en España, un caso real con equipos de trabajo en remoto
Lessons learned from contrasting Design Thinking and Agile Project Management...
Visual Scrum - What you see is What you get
Un Primer Paso a la Agilidad: Retrospectivas para el Aprendizaje de la Ingeni...
Análisis de la implementación de prácticas ágiles en Argentina
Como cocinar tu contrato ágil
Introducción a la agilidad
Cas2010 desarrollo-de-aplicaciones-en-la-nube-con-scrum-y-xp
Cas2010 gestion-agil-de-la-configuracion
Cas2010 itinerario-implementacion-agil
Cas2010 gestion-agil-de-equipos
Cas2010 integrando-practicas-agiles-y-de-experiencia-de-usuario
Cas2010 toolchain-for-agile-teams-traceability-from-product-vision-to-working...
Cas2010 los-principios-agiles-como-guia-o-por-que-querras-volver-a-modelos-tr...
Cas2010 to-track-defects-or-not-to-track-defects-that-is-the-question
Cas2010 is-there-space-for-testers-in-agile-projects
Cas2010 one-year-of-software-developments-to-win-a-world-racing-championship
Cas2010 pair-programming-strategies
Cas2010 behavior-driven-development-aplicado-en-acceptance-test-automation
Cas2010 herramientas-de-pruebas-unitarias-pex-y-moles
Ser ágil en España, un caso real con equipos de trabajo en remoto

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

  • 1. Quiero hacer ágil, ¿ y ahora qué: Java, Ruby o Scala ? Leo Antoli @lantoli Conferencia Agile-Spain 2011 - Castellón
  • 2. 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 ?
  • 3. Fuera de la sesión Lenguajes en cliente Frameworks Sólo se tratan algunos lenguajes
  • 4. Un lenguaje para dominarlos a todos
  • 5. 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
  • 9. 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)
  • 10. Entrega continua de valor: ¿ sólo con pruebas automáticas ?
  • 11. ¿ TDD/BDD obligatorio ? ... Anda antes de correr
  • 12. 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 ?
  • 13. 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
  • 14. 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
  • 15. 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;1\n2,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; ));     } } ...
  • 16. 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;5\n2\n3&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;1\n2,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;1\n-2\n-3\n4&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 ...
  • 17. 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
  • 18. 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
  • 19. 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
  • 20. 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  
  • 21. 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) ....
  • 22. 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)   } }
  • 23. 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 }
  • 24. 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
  • 25. 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
  • 26. JVM Groovy Scala JRuby Jython Clojure ... Quien lo iba a imaginar
  • 27. 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;
  • 28. 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
  • 29. 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 ?
  • 30. ¡WARNING! La siguiente sección contiene opiniones subversivas que pueden herir la sensibilidad
  • 31. 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
  • 32. 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)
  • 33. 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
  • 34. 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!
  • 35. 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....
  • 36. 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)
  • 37. 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
  • 38. 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?
  • 39. ¿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 !
  • 40. ¡YA ME VOY! Y ahora pasemos a las conclusiones (Gracias Leo!)
  • 41. 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