miércoles, 25 de agosto de 2010

Code Smells : Obsesión Primitiva

Queridos Chimpances Entrenados, Dice la Enciclopedia Galatica:

Obsesión Primitiva: Es un Smell que se produce cuando uno utiliza tipos primitivos como Strings o Doubles o Ints para representar conceptos que realmente merecen ser representados por clases de objetos.

Es uno de los smells más comunes que podemos encontrar en cualquier código que escribamos o revisemos.

Cuando se trabaja con Objetos, para modelar la estructura interna de una clase uno tiene dos alternativas:


1 - Referenciar a otra Clase de objeto

2 - Utilizar un tipo primitivo.

En general el problema empieza cuando uno decide utilizar un tipo primitivo para modelar un concepto porque se resiste a crear una clase para el mismo porque en ese momento solo sirve para encapsular un simple entero, una String o un par de estos, con nada o muy poco comportamiento extra.

Tipicos ejemplos de esto es cuando uno modela por ejemplo un rango de enteros, como ser, rango de valores válidos para un atributo para un sistema que modele rangos de edades especificos, campos de calle, numero y ciudad para modelar la direccion de un Cliente, etc. . Otro ejemplo es utilizar un int o String para discriminar el "subtipo" o diferentes variaciones de un objeto. Pero el caso emblematico por definición es cuando uno modela sumas de Dinero como un Double para representar el valor, y una String que representa la especie (dolares, pesos, euros, etc.)

Cuando uno sigue trabajando luego de la elección del tipo primitivo al principio siente que ahorro en cantidad de código y clases creadas, y se felicita por ello.

Pronto, no obstante, a medida que el sistema crece comienza a pedir más y más comportamiento de ese tipo primitivo, que, como en general no puede agregarse en el mismo, en lenguajes como Java o C# , se terminan ensuciando el código de la clase adonde se encuentra el primitivo, adonde se agregan métodos y más métodos helpers o utils que trabajan con ese tipo primitivo y con ninguno de los otros atributos de la clase.

Sin entrar en campañas proselitistas, otros lenguajes si permiten agregar métodos no solo a las clases de los primitivos, sino también a instancias específicas (prototype) como por ejemplo en Ruby o Smalltalk, pero no obstante las reglas de Buen Diseño esta indicando la necesidad de un objeto que represente mejor el concepto y que va a permitir agregarle comportamiento a medida que la solución la vaya necesitando (no olvidemos: nunca especular que se necesitará por adelantado).

Ejemplo 1
package ar.com.smells.examples; 
public class ClienteFibertel { 
private String nombre;  
private  String apellido; 
private MedioPago medioPago; 
private long idCliente; 
 
... 
 
private String nombreCalle; 
private String numero; 
private String piso; 
private String Depto; 
private String codigoPostal; 
private String ciudad; 
 
//Metodos propios de clase Cliente 
 
   .... 
 
//Metodos que piden a grito una clase Direccion 
 
    public boolean esCodigoPostalRural() { 
     bla bla bla 
    }
    
    public String direccionFormateadaParaEnvioPostal() {
            bla bla bla 
    } 
 
    public boolean esEdificio() {
           bla bla y mas bla. 
    }
 
} 
En este caso, se deberia refactorizar a una clase Direccion que forme parte del Cliente y que deberia llevarse los tres métodos que estamos indicando que no forman parte de la lógica propia de la clase adonde están.

Ejemplo 2

Un ejemplo muy especial dentro de la obsesion por primitivos, aunque no es el tipico ejemplo, es cuando se utiliza un int como subtipo y esto genera que se repita la lógica de distinción entre subtipos entre todos los métodos de la Clase. La solución es muy clara, la clase debe separarse en una clase base y varias subclases con la diferencia.

 
public class ConexionInternet
{ 
   ... 
 
private int tipoConexion;
 ... 
 
    public String metodoA() { 
         if (tipoConexion == ConexionInternet.CABLEMODEM) { //ya no  hay mas, adios Fibertel
 
         } else if (tipoConexion == ConexionInternet.ADSL) { // speedy? arnet? }
 
           } else if (tipoConexion == ConexionInternet.Modem) { //El unico que va a quedar, la vuelta al 56K! 
 
             }
   } 
 
   public boolean metodoB() {
       if  (tipoConexion == ConexionInternet.CABLEMODEM) { //ya no hay mas, adios Fibertel
     
       } else if (tipoConexion == ConexionInternet.ADSL) { // speedy? arnet? 
 
       
        } else if (tipoConexion == ConexionInternet.Modem) { //El unico que va a quedar, la  vuelta al 56K! 
 
        } 
   }
 
} 

Es bastante lógico decir que parece razonable refactorizar en 3 clases ConexionInternetCableModem, ConexionInternetADSL, ConexionInternetModem, todas dependiendo de una clase abstracta o implementando una interfaz ConexionInternet.

De cualquier manera una de los ejemplos más clásicos y que mayores problemas trae al estar “Obsesionado por Primitivos” es cuando se modelan Sumas de dinero como objetos Double. Los que escribimos este blog damos plena fe de ello.

El primer problema aparece cuando uno comienza a realizar operaciones y surgen las "pequeñas" diferencias por redondeo!. Y claro, cuando uno tiene problemas de redondeo con kilometros entre planetas no hay mucho problema (salvo que este manejando una nave espacial :), pero si es dinero... ahi sii, esos centavos importan y como!

Luego la siguiente estación al desastre es cuando el Usuario, a pesar de habernos dicho que siempre trabajaba con dinero local (pesos, pesos Chilenos, reales, lo que sea) viene muy contentos a contarnos que ahora va a empezar a aceptar pagos en dolares, euros, y en… rupias indias!.

Y nosotros con la clase Producto definida asi:

 
public class Producto {
... 
private double precio; 
 
.... } 

Inmagínense el cambio en todo el sistema!

La solución es ampliamente conocida y no nos vamos a extender en ella por temor a hacer un post kilométrico, pero para los que les interesa pueden verla en detalle en el libro de Test Driven Development (Lo se, lo hemos recomendado muchas veces, de Kent Beck).

Para los ansiosos o remolones, la solucion es crear una clase Money que siga el patrón de diseño Value Object, sin mostrar el detalle de los métodos, sería algo asi:

 
public class Money { 
 
private BigDecimal monto; 
private Currency moneda; 
….
} 

Un hecho relativamente moderno que ha reafirmado e incentivado la Obsesión Primitiva es la utilización másiva de ORMs (Object Relational Mapping), como Hibernate.

El problema en este caso es cuando se esta definiendo un Bean que terminará como tabla en una base de datos y, y condicionados por la complejidad de la herramienta, se prefiere tener un bean al cual le agregan los atributos primitivos directamente y no múltiples tablas de objetos relacionados.

Por ejemplo, prefieren tener una sola clase SujetoAnses al cual le agreguen 2 int para guardar rango máximo y mínimo configurado a tener otra tabla relacionada con el Bean principal con los rangos permitidos.

O del ejemplo anterior, ClienteFibertel con todas las String en una misma tabla a tener una relacion con otra tabla Direccion. Es inútil explicar en estos casos que en general no es razonable decidir el Diseño de Objetos por las consecuencias que tendrán sobre la base de datos. Al menos no a priori hasta tener razones de peso, de performance, etc..

Para terminar, no estamos afirmando que TODO atributo o concepto simple debe ser representado como una clase de objetos que se convierta en simples wrappers de un int, String o double.

Pero ante el minimo comportamiento que haya que agregar, cuando uno comienza a pasar ese valor de aquí para allá o hace algún método que lo utiliza solo a él, etc., en esos casos mantenerlo como primitivo es… una obsesión y hay que tratarla!.

lunes, 16 de agosto de 2010

Test de unidad con Selenium

Hace algunos días que estoy automatizando un conjunto de Smoke Tests, es decir tests que se ejecutan para ver si algo se rompe al hacerlo, sin verificar que el resultado de las operaciones sea el esperado. Estos tests se ejecutaban antes a mano, con el esperable desperdicio de recursos.

Para la automatización decidí usar Selenium, por ser una de las herramienta open-source más populares y porque tiene muy buena portabilidad entre browsers. Selenium es una librería Javascript que se agrega a nuestra aplicación Web (en forma transparente, ya vamos a ver como) y que la extiende con una interfaz capaz de recibir comandos que permiten interactuar con los elementos gráficos de la UI.

Existen varias maneras de utilizar Selenium. La más sencilla es Selenium IDE, que permite crear tests mediante el proceso de "Record & Play", es decir que tiene la capacidad de registrar nuestra interacción con un sitio web y de reproducir luego exactamente las mismas acciones. Esta modalidad no exige saber nada sobre el funcionamiento de Selenium y permite que una persona sin conocimientos de programación pueda escribir sus propios tests. Sin embargo tiene el grave incoveniente de que los tests así creados son muy frágiles: cualquier cambio en la aplicación causa que los tests dejen de funcionar y deben ser creados de nuevo desde cero (este problema no es particular de Selenium IDE si no que es compartido por todas las herramientas que utilizan el esquema "Record & Play").

Debido a estas limitaciones decidí utilizar Selenium Remote Control. Esta solución tiene una arquitectura de cliente/servidor. El servidor se encarga de controlar una instancia de un Web Browser (puede ser Internet Explorer, Firefox, Opera, Chrome y Safari). El cliente interpreta los tests y envía comandos al server, que se ocupa de ejecutar los comandos recibidos en el browser que controla. Esta arquitectura permite al servidor injectar las librerías Javascript que componen el core de Selenium en forma transaparente, sin que tengamos que instalarlas en el servidor de la aplicación que queremos testear como en versiones anteriores.

Como la interfaz entre el cliente y el servidor es HTTP puro, los clientes (y por lo tanto los tests) pueden estar escritos en cualquier lenguaje capaz de ejecutar un request HTTP. Existen clientes para C#, Java, Perl, PHP, Python y Ruby.

Veamos un ejemplo sencillo de como utilizar Selenium RC con Ruby. En primer lugar es necesario realizar la instalación de Selenium. Para el server, solo es necesario bajar y descomprimir este archivo y tener disponible un run-time de Java.

Para instalar el cliente ruby, podemos utilizar el siguiente comando:

gem install selenium-client

Una vez realizada la instalación, hay que poner a correr el server:

cd selenium-server-1.0.3 java -jar selenium-server.jar

Y podemos probar este pequeño script de prueba, que ejecuta una búsqueda en Google:

#hello_google.rb require "selenium/client"

begin @browser = Selenium::Client::Driver.new \ :host => "localhost", :port => 4444, :browser => "*firefox", :url => "http://www.google.com", :timeout_in_second => 60

@browser.start_new_browser_session @browser.open "/" @browser.type "q", "Selenium seleniumhq.org" @browser.click "btnG", :wait_for => :page puts @browser.text?("seleniumhq.org") ensure @browser.close_current_browser_session end

Este script crea una instancia del cliente Ruby y ejecuta los comandos necesarios para escribir la frase "Selenium seleniumhq.org" en el cuadro de texto de google y a continuación presiona el boton de búsqueda.

El código que se ocupa de escribir el texto es un ejemplo muy representativo de como funciona Selenium asi que vamos a utilizarlo para entrar un poco más en detalle.

@browser.type "q", "Selenium seleniumhq.org"

El segundo parámetro del método type es solo el texto a cargar y por lo tanto no es muy interesante. El primer parámetro en cambio es el mecanismo para localizar el objeto en que queremos escribir (locator en la jerga Selenium) y es muy importante, ya que la identificación en forma efectiva y robusta de los distintos componentes de la interfaz es fundamental en estos trabajos de automatización.

En este caso el locator es simplemente el id del text box. Esta forma de identificación es claramente la más sencilla y probablemente la mejor desde el punto de vista de la mantenibilidad. Sin embargo, muchas veces no es posible utilizarla, ya sea por que los objetos que queremos identificar no tienen id o porque el id de un mismo objeto cambia entre sesiones. Para estos casos, Selenium tiene formas más poderosas de identificacion.

Por ejemplo podemos identificar objetos utilizando XPath (http://en.wikipedia.org/wiki/XPath), que es un lenguaje para acceder a las distintas partes de un documento escrito en un lenguaje de markup. Veamos algunos ejemplos de selectores XPATH

Escribe en el elemento de tipo input con el id "q" (equivalente al ejemplo anterior) @browser.type "xpath=//input[@id='q']", "texto a buscar"

Identifica un div con el texto "Hola", pero hace click en otro div, que es "hermano" del identificado @browser.click "xpath=//span[text()='Sales']/../div"

XPATH es muy poderoso y un tutorial completo escapa a los objetivos de este artículo. Pueden encontrar una introducción aquí: http://www.w3schools.com/xpath/xpath_intro.asp . Un detalle importante para la utilización de XPath con Selenium es que no todos los browsers proveen una buena implementación de XPath, por lo que muchas veces conviene pedirle a Selenium que utilice su propia versión de la herramienta, que es un poco más lenta pero más poderosa.

Otra alternativa para identificar objetos con Selenium es CSS. En este caso la forma de nombrar los objetos es exactamente la que utilizamos para describir su presentación en archivos CSS. Veamos algunos ejemplos:

Escribe en el elemento de tipo input con el id "q" (equivalente al primer ejemplo XPath) @browser.type "css=input#q", "texto a buscar"

Click en el link que contenga el texto "Mi Link" @browser.click "css=a:contains('Mi Link')

Una ventaja importante de CSS sobre XPath es que en Internet Explorer corren muchísimo más rápido los comandos que utilizan CSS.

Como conclusión, Selenium me pareció una herramienta muy interesante. Con un poco de práctica se pueden escribir tests con bastante facilidad y esos tests se pueden utilizar después en distintos browsers prácticamente sin modificaciones. En artículos posteriores vamos a hablar de buenas prácticas en la escritura de tests con Selenium y también de la utilización de algunas herramientas para hacer más fácil la detección de formas de identificar los componentes de la UI con los que queremos interactuar.

martes, 10 de agosto de 2010

El camino del Conocimiento: de Aprendiz a Maestro

El problema en cuestión es: Como encarar la adopción de una metodología ágil?

Artes Marciales y un poco de historia

Como siempre, la cultura milenaria Japonesa viene en nuestra ayuda. Existe un concepto dentro del arte marcial japonés llamado ShuHaRi, que describe, dentro del camino hacia el conocimiento, 3 etapas necesarias para volverse un maestro:

ShuHaRi
Es la composición de tres palabras: Shu - Ha - Ri

Shu significa "proteger" y "obedecer", con significado de obedecer el "conocimiento tradicional". El aprendiz debe aprender las técnicas y conocimientos fundamentales, bajo la supervisión de un maestro. Todavía no esta listo para explorar y comparar diferentes caminos.

Ha significa "desprenderse", "desamarrar", "derivar". Es romper con la tradición. De una manera controlada el "Practicante" comienza a explorar diferentes caminos.

Ri es "dejar", "separarse", "trascender". Ya no hay un seguimiento de reglas o técnicas porque todo movimiento es natural y fluido. El conocimiento forma ya parte indivisible con la persona que se ha transformado finalmente en un Maestro.

Toda persona que comienza a aprender este arte marcial debe pasar por estas tres etapas que lo llevan desde aprendiz a Maestro.

En la cultura occidental , durante la edad media los aspirantes a determinados oficios (herrero, carpintero, etc.), recorrían un camino similar a ShuHaRi, adonde cada aprendiz empezaba a trabajar con un Maestro, con el tiempo se convertía en un profesional, practicante de su oficio y luego de muchos años y una mejora constante llegaba a tomar el lugar de Maestro y completaba el ciclo teniendo sus propios aprendices.

Adopción de metodologías ágiles

Desde la irrupción masiva de las metodologías ágiles en el mundo del desarrollo de software y su llegada a nuestro país hemos presenciado varias veces el momento en el cual una empresa comienza a aplicar ágiles.

Ignoramos si solo es una característica de nuestro país, o es general pero observamos la misma falla una y otra vez:

Se define el cambio a una nueva metodología, tomemos con ejemplo a Scrum, se capacitan a las personas, se contrata a coachs, se eligen equipos, etc., pero se comienza a aplicarlo con modificaciones a la esencia de la metodología. Es decir, se saltean la etapa imprescindible del aprendizaje, la de Aprendiz (Shu) y pasan directamente a Ha.

Por supuesto, estas modificacione se producen esgrimiendo variadas y al parecer muy validas razones, respaldadas por las excusas más variadas, como por ejemplo:

  • "En esta empresa hacemos las cosas diferentes".
  • "No podemos seguir la metodología al detalle, acá somos muy especiales".
  • "El de Product Owner es un concepto de lo más interesante pero... acá los usuarios son difíciles. No va a servir."
  • Aca los usuarios están muy ocupados.
  • No, aquí esto va a traer problemas (así, de una manera difusa, en voz baja, con miradas nerviosas por sobre el hombro).
  • No se adapta a la cultura de la empresa.
  • Este principio no aplica a nuestro entorno porque va en contra de nuestra politica X.
  • etc.

Se plantean así desde el primer momento "adaptaciones" a la metodología. Adaptaciones estas que son cambios encubiertos, que de una u otra manera van en contra de los principios por los cuales la metodología funciona.

Es decir, se busca la excusa que sea necesaria para cambiar para no cambiar. Se hace cualquier modificación de la forma de trabajar que no implique un cambio real en la forma de trabajar.

Porque justamente todo cambio Real implica entrar en una zona de incomodidad, de no confort y eso generalmente cuesta, cuesta mucho.

Vale una aclaración importante, no estamos proponiendo no hacer ninguna adaptación a la realidad en la que nos movemos, a la cultura adonde estamos inmersos y ser talibanes al imponer rigurosamente cada punto y coma de la metodología en cuestión. Cuestiones acerca de si entrar de lleno con todas las prácticas o el grado de profundidad que aplicará desde entrada, si incluir hasta la ultima práctica recomendada de entrada o ir haciendolo de manera progresiva, la forma o detalles de implementación de las prácticas técnicas, etc, son todos puntos a pensar y adaptar de la mejor manera para el contexto y proyecto en cuestión.

Pero, el punto esencial es que si uno eligió una metodología ágil no puede proponer cambios que vayan en contra de los principios y valores sobre los cuales esta basados esa metodología. Así de sencillo.

El camino del aprendizaje

Miremos, por otra parte, como propone encarar la adopción de una metodología ágil, James Shore, un muy conocido evangelizador de metodologías ágiles, fundamentalmente de XP, y autor del libro "The Art of Agile Development".

En su "Camino del aprendizaje" justamente basado en ShuHaRi propone encarar el estudio de un método nuevo, como es, adoptar una nueva metodología de desarrollo, de la siguiente manera:

1 - Seguir las reglas: Esta es la etapa inicial durante la cual se elige un metodo y se lo sigue tan rigurosa y detalladamente como sea posible. Durante esta etapa uno esta aprendiendo del tema en cuestion, asi que debe estudiar, debe seguir el metodo, reflexionar sobre la experiencia y volver a estudiar. Es importante justamente seguir las reglas fielmente, en su espiritu al menos, para poder evaluar si nos sirve o no. En lo que respecta a este tema uno es un aprendiz.

2 - Romper las reglas: En esta etapa, habiendo uno realmente aprendido el metodo (sin modificaciones "tempranas") y haberlo practicado durante un cierto tiempo se plantea una etapa de mejora basada en pequeños cambios controlados. Uno debe evaluar los resultados obtenidos, plantear un solo cambio y volver a prácticar el metodo completo, para poder medir la diferencia del cambio realizado. Durante esta etapa uno ya ha pasado la etapa de aprendiz y sabe tanto del método como para poder ajustar determinadas partes sin ir en contra de los principios que lo hacen funcionar. Es ya un Practicante del método.

3 - Ignorar las reglas: Por último en esta etapa el conocimiento se incorporó completamente y las acciones a realizar surgen naturalmente respetando fielmente los principios y valores subyacentes. Uno no debe pensar ni recordar ya en las reglas de manera individual porque las tiene internalizadas de manera que se aplican constantemente pero adaptandolas instantaneamente al problema en cuestión. Se hace lo que le parece correcto basándose en su amplia experiencia y el conocimiento del método que absorbió y luego se observa y actúa en base al resultado.

El Practicante se ha convertido en Maestro.

Nota de los autores: Queremos agradecer a James Shore por su permiso para traducir, publicar y discutir su poster "camino del conocimiento, así como también su excelente predisposición para ayudarnos. Recomendamos fuertemente cualquier libro o artículo proveniente de Él.

Author's note: We would like to thanks James Shore for his permission to translate, post and discuss his poster "The Road to Mastery" and for his help and feedback. We strongly recomend any of his books or articles.

lunes, 2 de agosto de 2010

Code Smells

De chiquitos aprendemos que hay que evitar lo que tiene mal olor. La madre que huele el pañal de su hijo sabe que si apesta tiene que cambiarlo, y si apesta mucho, mucho, quizás el bebe tenga algún problemita de diarrea o algo similar. Sentido común universal este que sin embargo no aplicamos frecuentemente en el desarrollo de software.

A lo largo de la corta pero no menos exitosa vida de este Blog hemos mencionado varias veces a los Code Smells, sin aclarar su significado, mea culpa, porque a veces damos por sentado conceptos que son muy útiles. Este post entonces intenta corregir semejante falta:

Code Smells: (Aplicado al arte de la programación) son todos los síntomas que podemos encontrar en el código fuente de un sistema que indican que muy probablemente existan problemas más profundos de calidad de código, de diseño o de ambos.

El termino fue acuñado por Kent Beck y Ward Cunninghan hacia fines de los 90 y su uso se popularizó a partir del libro de Martin Fowler y otros: "Refactoring, Improving the Design of the existing Code" .

Si pensamos las barbaridades que hemos visto en algunos códigos fuentes (siempre de compañeros, nunca nuestros... :O ) y asociamos esa sensación de horror y rechazo con el olor a podrido de algunos ríos contaminados (Nota para los argentinos: Iba a poner Riachuelo pero no se si el resto del mundo conoce la fama "olorosa" de nuestro querido río, querido porque esta en la Boca ;), decía, esa imagen gráfica claramente lo que significa Code Smell, síntoma de que algo Apesta, y como al pañal, hay que cambiarlo.

En la práctica, históricamente, los Code Smells se han usado para identificar partes del código de baja calidad a las que aplicar las técnicas de refactoring explicadas en el libro arriba mencionado. Es decir, el libro describe un conjunto largo de distintas técnicas de transformación de código sin alterar su significado, las asi llamadas "Técnicas de Refactoting" y los Code Smells son los que indican en que parte del código hay que revisar como candidatas para aplicar esas técnicas.

No es la intención de este post describir uno a uno los Code Smells porque eso llevaría demasiada "tinta virtual" pero prometemos ir describiendo los Smells principales ya que consideramos (y bueno, no seria nada nosotros pero también lo piensa la gente más brillante de nuestro campo de la programación) que es uno de los conceptos fundamentales de la programación de estos tiempos, de los más aplicables y útiles, en combinación con los tests de unidad y que mayor impacto tienen tanto para la productividad como para la calidad de los sistemas que generemos.

Los principales Code Smells que se pueden encontrar en el código son :

  1. Código Duplicado
  2. Parámetros Booleanos
  3. Métodos Largos
  4. Clases Largas
  5. Larga Lista de Parámetros
  6. Cambio Divergente
  7. Shotgun Surgery (traducido algo asi como "cirugía a escopetazos")
  8. Feature Envy (textualmente: "envidia de features")
  9. Data Clumps (algo asi como aglomeracion de datos, masa de datos, en sintesis: un bodoque de datos (Arg))
  10. Obsesión por primitivos
  11. Generalización especulativa
  12. Campos temporales
  13. Refused Bequest ( algo asi como Rechazo a recibir parte de una herencia)
  14. Etc. (porque hay muchos mas).

Sigan sintonizando nuestro canal http://trainedchimpanzees.blogspot.com , porque vamos a estar analizando los mismos en sucesivos posts. De hecho ya empezamos a describir los Code Smells, uno de los primeros posts que hicimos fue sobre el Smell "Parametros Booleanos".

Para terminar una frase atribuida a la abuela de Beck, tómenla como consejo:

"Si apesta, hay que cambiarlo!"

miércoles, 28 de julio de 2010

Configurando un nuevo entorno de desarrollo

Estoy empezando un nuevo trabajo y esta semana le estuve dedicando un poco de tiempo a configurar mi nueva laptop. Este es siempre un proceso importante para nosotros los programadores, ya que pasamos muchas horas por día usando nuestras máquinas y por lo tanto todo lo que haga más confortable el entorno de trabajo influye mucho en nuestra productividad.

En esta oportunidad decidí llevar un log del proceso de instalación, por un lado para que me quede como documentación si necesito volver a instalar pero principalmente para compartirlo aquí y recibir feedback sobre herramientas o configuraciones utilizadas por otras personas.

Como voy a estar desarrollando aplicaciones que corren en Ubuntu decidí instalar ese sistema operativo. Además (y sin querer entrar en una guerra religiosa), me parece un entorno mucho más productivo para un desarrollador.

La instalación de Ubuntu 10.4 sobre mi Dell Vostro 1320 fue bastante fácil, solo tuve que tomar en cuenta algunos detalles. Por un lado hay que realizar la instalación conectado vía cable (no wireless) a Internet, para que se baje ciertas actualizaciones claves sin las cuales no funciona bien. Por otro lado, si bien los drivers propietarios de Dell funcionan correctamente, hay que eliminar uno de ellos que se instala por default y que causa problemas en el manejo del switch de la red wireless. Para hacerlo hay que agregar la siguiente linea al archivo /etc/modprobe.d/blacklist.conf.

blacklist dell_laptop

A continuación, y ya con la laptop andando correctamente en Ubuntu, me puse a instalar las herramientas que normalmente utilizo. Por supuesto, empecé con gvim (la instalación standard de ubuntu incluye la versión de consola de vim, pero yo uso también la version con interfaz gráfica).

sudo apt-get install vim-gnome

En seguida instalé vimwiki. Esta herramienta es un wiki personal que funciona como un plugin de vim. La función de un wiki personal es organizar nuestra información de la misma manera que haríamos en un wiki de un grupo o proyecto. Personalmente yo lo uso para tomar notas de las cosas que voy aprendiendo o quiero recordar más adelante (por ejemplo estoy armando este artículo en bases a las notas que tomé en vimwiki mientras realizaba la instalación). También me resulta muy útil para ir llevando un diario que me permita saber a que le dediqué mi tiempo cada día y para mantener una lista de las cosas que tengo pendientes de hacer. Más allá de la herramienta que se use (hay muchas versiones diferentes de Wikis personales), me ha resultado una práctica muy útil.

Instalar vimwiki es muy sencillo siguiendo las instrucciones del sitio. Solo hay que tener en cuenta que se necesita agregar las siguientes lineas al archivo ~/.vimrc

set nocompatible filetype plugin on syntax on

Una vez instalado gvim, me dediqué a la configuración del interpreté de linea de comando (bash en mi caso, tengo ganas de probar zsh algún día). En primer lugar lo configuré para poder utilizar los comandos de vi en la edición, agregando las siguientes dos líneas al archivo ~/.inputrc :

set editing-mode vi set keymap vi

Después hice que la historia de comandos del shell fuera prácticamente infinita (lo que suele ser muy útil cuando uno quiere encontrar tareas para automatizar). Para eso agregué al archivo ~/.bashrc las siguientes líneas:

HISTFILESIZE=1000000000 HISTSIZE=1000000

De manera que el shell va a recordar el último millón de comandos que ejecuté!

A continuación me dediqué a configurar el teclado. La distribución de teclado latinoamericano que tiene mi Dell anda perfecto, pero hay un par de teclas que siempre me interesa reconfigurar. En primer lugar, desactivé la tecla de bloqueo de mayúsculas (Caps Lock). Nunca la uso y a veces la toco por error causando algunos problemas (sobre todo en vi, que es *muy* case sensitive). Lo que hice fue transformarla en otra tecla Alt-gr, para lo cual agregué las siguientes dos lineas al archivo ~/.Xmodmap.

remove Lock = Caps_Lock keysym Caps_Lock = ISO_Level3_Shift

La otra modificación que hice al teclado fue habilitar la tecla de Windows, para poder usarla en combinaciones de hotkeys. Para eso agregué las siguientes tres lineas al archivo ~/.xstartup

xmodmap -e "remove mod4 = F13" xmodmap -e "keycode 115 = Super_L" xmodmap -e "add mod4 = Super_L"

Una vez hecha esta modificación podemos utilizar las combinaciones básicas de windows (Windows + L para lockear la máquina, por ejemplo) siguiendo estas instrucciones. Además instalé tres plugins para Firefox: Firebug (fundamental para desarrollo Web), Down them all (un muy cómodo manejador de downloads) y Vimperator (que configura Firefox para poder navegar utilizando los comandos de Vim!! Creo que mi relación con Vim está dejando de ser saludable :) ). Finalmente, para poder utilizar algunos programas de Windows (fundamentalmente Windows Live Writer, para el cual no conseguí un equivalente Ubuntu), instalé VirtualBox, la máquina virtual de Sun y cree una maquina virtual Windows.

Esta fue mi customización básica de Ubuntu. A medida que pasen los días, seguramente voy a ir instalando algunas cosas nuevas y ajustando Ubuntu para automatizar todas las tareas repetitivas que pueda, algo que se facilita por ser Linux un sistema operativo muy amigable para los desarrolladores. Como decía un viejo chiste , no es que Unix no sea user-friendly, solo que es muy selectivo con quienes considera sus amigos.

jueves, 22 de julio de 2010

¿Cuántos libros técnicos leiste este año?

"Tenes que leer este libro y presentarlo"

El primer libro técnico que leí por motivos laborales fue "Code Complete", de Steve McConnell, en el año 1996. En el trabajo en que estaba en ese entonces me dieron la asignación de leer el libro y presentar partes de el a mis compañeros. Empecé la tarea con pocas ganas, pero pronto me di cuenta de que la lectura de este libro iba a cambiar mi vida profesional para siempre. La posibilidad de "pararme en los hombros de un gigante" al aprender de alguien con tanta experiencia como McConnell me fascinó y a partir de ese momento he intentado siempre leer la mayor cantidad de libros técnicos posible.

Libros y capacitación

Un libro es probablemente la manera más económica de capacitarse. "The Pragmatic Programmer" de Andy Hunt y Dave Thomas (un libro excelente, si van a leer un sólo libro este año, lean este) cuesta 40 dolares en Amazon. Tomando en cuenta el envío probablemente el costo total debe rondar los 200-250 pesos. ¿Qué curso se puede tomar por ese precio, aun suponiendo que fuera posible encontrar uno que valga la pena?. Si se comparan las versiones electrónicas de los libros los costos son aún menores, ya que cuestan menos y además no hay que pagar envío.

Por lo tanto para una empresa que le de importancia a la capacitación, es posible lograr muy buenos resultados invirtiendo unos $1000 por mes. Por supuesto hay que tomar en cuanta que con sólo comprar los libros no alcanza y que hay que buscar maneras de lograr que los libros sean realmente leídos y aplicados. Una práctica que da muy buenos resultados es que los miembros de un equipo se compromentan a leer un libro y armar almuerzos quincenales o mensuales donde todos puedan discutir algunos capítulos o armar presentaciones y resúmenes.

¿Sigue valiendo la pena invertir en libros con toda la informacion disponible en la Web?

Depende del tipo de libro. Los libros de referencia probablemente no sean una buena inversión: es mucho más sencillo usar Google (y más barato, una vez compramos un libro de referencia sobre Java, llamado "Java Bible", fue muy útil....como soporte de la pata de una mesa). Sin embargo los libros que tratan sobre un tema desde una perspectiva más completa, si lo son. Por poner un ejemplo, no tiene sentido buscar como hacer paginación en Rails en otro lado que no sea Google (o Bing!, ;) pero si uno quiere entender realmente como funciona Rails solo lo va a poder hacer desde un libro, donde los conceptos van a estar presentados de una manera mucho más integral (y al respecto recomiendo el excelente "The Rails Way"). Esto tiene que ver con lo que contábamos en "cargo cult programming": para buscar una solución rápida a un problema probablemente el mejor recurso es la Web pero para entender realmente por qué funciona esa solución lo mejor es un libro (o un curso, claro que tomar un curso en EEUU o Europa no es para cualquiera).

Spik in inglish?

Una dificultad adicional que tenemos en países donde no se habla inglés es el idioma. El porcentaje de buenos libros técnicos traducidos al castellano es muy bajo y la calidad de las traducciones es muy mala (nunca voy a olvidar la traducción de buffers como "tampones de memoria" en un libro de redes de la época de la facultad). Esto hace que la incapacidad de leer correctamente inglés sea un escollo insalvable para ser un buen profesional. Si alguien que no sepa este idioma quiere dedicarse al desarrollo de software su mejor inversión (mejor que aprender cualquier tecnología) va a ser dedicarse a aprenderlo.

cuanto$?

Los programadores somos trabajadores intelectuales y por lo tanto nuestros conocimientos constituyen nuestro capital. Si dejamos de actualizarnos nos descapitalizamos y perdemos la posibilidad de conseguir mejores trabajos y ganar más dinero. Dedicar una pequeña parte de nuestro tiempo a leer libros es una manera de aumentar nuestro valor en el mercado y mejorar a la vez profesionalmente.

Entonces, ¿Cuántos libros técnicos leiste este año?

lunes, 19 de julio de 2010

POO - Ejemplos de la ley de Demeter

Para dar un poco más de sustancia y aclaración al posteo del otro día sobre leyes y principios de Programacion Orientada a Objetos vamos a ir armando pequeños ejemplos de aplicación de los mismos.

Seguro no tenemos la menos necesidad de repetir porque Toooodoos recuerdan la ley de Demeter que decia:

 Un método m, de una clase C, solo debe invocar métodos:

  1. De la misma clase C (métodos privados)
  2. De un objeto creado en m (Variable local)
  3. De un parámetro, pasado como argumento a m (parámetros)
  4. De una variable de instancia de C. (Variables de instancia de la clase

En otras palabras, un objeto no debe conocer la estructura interna de los objetos directamente relacionados con él, ni obtener a través de ellos a otros objetos para manipularlos o enviarles mensajes.

Para ser no-innovadores vamos a usar un ejemplo clásico de la ley de Demeter, conocido como el ejemplo del chico-Diariero (PaperBoy). La clase en nuestro ejemplo la nombramos simplemente Diariero. El ejemplo se basa en la relación entre El Diariero y el Cliente del diario en el momento en que se quiere cobrar tan solo un diario entregado.

Una posible implementación que viola el principio de Demeter (y que la hemos visto utilizada en código de proyectos reales, con ligeras variaciones, una y otra vez) es :

 
 
public class Diariero 
{
private static final 
double COSTO_DIARIO = 3.5;
 
  public Money cobrarDiario(Cliente cliente) {
    return cliente.getBilletera().extraer(COSTO_DIARIO);
  }
}

Es decir, la clase Diariero usa al parametro Cliente, le pide su billetera y extrae de ahi directamente el costo del Diario (el cual, para simplificar aún mas es una constante).

Aqui se puede ver fácilmente porque se viola el principio de Demeter de una manera bastante gráfica. El diariero está literalmente "metiendole la mano en el bolsillo" al cliente, y sacandole la plata. De una manera coloquial a esto es exactamente lo que apunta a evitar el principio. Que los objetos no "violen" los limites de las responsabilidades asignadas ni generen acoplamientos innecesarios. Que pasaría en este ejemplo si el cliente nos quiege pagar con algún otro medio de pago, por ejemplo, con Cheques? Habría que modificar ambas clases, Cliente y Diariero (y quizás algunas más).

Es interesante ver que el uso de getters tipo getBilletera es muy común y no causa sospechas en nadie (de hecho muchas ides tienen forma de generarlos automáticamente), pero que si en vez de usar un getter usáramos variables de instancia públicas tendríamos inmediatamente a la policía de los Objetos pidiendo nuestra captura. Sin embargo, el acoplamiento causado por este uso de los getters es casi el mismo que si usáramos variables públicas (y ni hablar cuando no solo tenemos getters sino que también tenemos setters!).

Veamos por otro lado una implementación que respeta la ley de Demeter :

 
public class Diariero {
   private static final double  COSTO_DIARIO = 3.5;
 
   public Money cobrarDiario(Cliente cliente) {
     return cliente.cobrar(COSTO_DIARIO);
   }
}
 
public class Cliente {
   private Billetera billetera;
 
  private Billetera getBilletera() {
     return billetera;
  }
 
  public Money cobrar(double costoDiario) {
      return getBilletera().extraer(costoDiario);
  }
 
}

Notar que en este ejemplo, el método getBilletera permanece privado (es algo privado justamente de cada Cliente), y no es accedido directamente. El diariero le pide al cliente que le dé el dinero a través del método cobrar. Luego este método es el que resuelve como obtener el dinero, por ahora extrayéndolo de la billetera, pero si quisiera podria devolver un cheque (deberia ser una subclase de Money, quizás) o algún otro medio de pago, sin necesidad de modificar la lógica de la clase Diariero.

En general se debe seguir la Ley de Demeter pero hay que tener cuidado con hacerlo demasiado al pie de la letra. Demeter apunta a reducir las llamadas del estilo a.b().c().d() (los clásicos "choques de trenes") , que claramente indican un problema, pero también hay que tener cuidado con llenar las clases de métodos que solo funcionan como intermediarios entre objetos. Al respecto Martin Fowler dice que estaría más correcto llamar a esta regla la Sugestion de Demeter, ya que no es algo absoluto.