22 diciembre 2011

"Developability"

Existen numerosos atributos de calidad, e incluso clasificaciones estándar de ellos.

¿Para que sirven estas clasificaciones?

Por un lado sirven para darle a los diseñadores de sistemas un punto de partida. Algo que permita dar respuesta a la pregunta: "entre las características que requiere nuestro sistema ¿nos estaremos olvidando de algo?". Por otro sirven para que los investigadores de arquitectura de software puedan definir métricas y estilos de solución.

Y por otro, sirven para que yo tenga una excusa por donde empezar este post :)

Estaba pensando que en estas clasificaciones hay un atributo de calidad perdido, que voy a bautizar como "Developability", y voy a definir así: la capacidad un sistema para reflejar los cambios introducidos durante el desarrollo.

Después de todo si en Java existen productos como JRebel y frameworks como Play anuncian sus bondades con frases como: "No need to compile, deploy or restart the server"; creo que existe una necesidad de tener la característica de "developability" en mente a la hora de pensar en el diseño de un sistema.

¿No es lo mismo que Maintainability?
Es parte de la mantenibilidad, en el sentido que forma parte de la facilidad de cambio de un sistema.

Pero muchas veces cuando se habla de mantenibilidad se hace referencia a cuan fácil es introducir nueva funcionalidad sin tener que hacer grandes cambios en el diseño.

Podemos tener, por ejemplo, un sistema muy fácil de extender, donde cada vez que un desarrollador introduzca un cambio tenga que esperar 2hs a que termine la compilación para poder verlo.

¿No es lo mismo que Testablity?
Testabilty se refiere a que tan fácil es verificar el software, por ejemplo: si es posible realizar test automatizados con facilidad o no.

Y lo que planteo es en cierto sentido similar, pero con objetivos levemente distintos: un sistema puede ser fácil de testear en el sentido que es posible automatizar tests u obtener información de estado relevante para el testing y aún así ser un tremendo "dolor" para desarrollar.

Un ejemplo: Mejorando el "developability" de Microwiki
Mi pequeño proyecto: microwiki; del que les conte hace un par de posts atras. Utiliza templates de html para renderizar las paginas web. Estos templates son resources y se cargan del classpath.

Por default Jetty no hace un reload del classpath cada vez que algo cambia -principalmente por que estar viendo el filesystem por cambios es una operación costosa.

Al principio tener que reiniciar microwiki para ver los cambios en un template no me molestaba: el proyecto es chico y reiniciar es rápido.

Sin embargo cuando empece a mejorar un poco el diseño del HTML esos segundos de presionar restart en el IDE y volver a cargar la página se volvieron una molestia.

Por suerte para no meterme con cuestiones de classpath reload y configuración de Jetty, algunos astros de "mantenibilidad" y "testeabilidad" se alinearon.

Para permitir que microwiki sea configurable mediante archivos de configuración, y que la interpretación de estos me sea fácil de mantener utilice un DSL de Groovy.
Es decir los archivos de configuración son código fuente Groovy, y en ellos es posible definir un template de la siguiente manera:

templates {
    display = '/dir/disp.html'
    edit = '/other/edit.html'
}

Por otro lado para facilitar el testing los templates implementan la interfaz: ViewTemplate. Esto me facilita la creación de mocks en los tests.

Juntando estas dos cosas, el cambio para facilitar el "developability" fue muy sencillo:

Agregue un método al builder que interpreta el DSL del archivo de configuración (ConfigBuilder):

ViewTemplate debug(source, Map context = [:]) {
  { Map ctx -> template(source, context).applyWith(ctx) } as ViewTemplate
}

Con este método es posible definir la configuración de esta manera:

templates {
 File prjDir = new File('/Users/diegof/Projects/microwiki/src/main/resources')

 edit = debug(new File(prjDir, 'microwiki/templates/edit.html'))
 search = debug(new File(prjDir, 'microwiki/templates/search.html'))
}

Y ya no hay que preocuparse por el reload: microwiki va a leer el archivo cada vez, y los cambios se ven de forma instantánea (claro que esto no tiene en cuenta los cambios en el stylesheet, aunque es un avance).

De que sirve esto... cuando tengan uno de esos proyectos web que cargan millones de cosas al inicio, es bueno dedicar un poco de tiempo a pensar como mejorar el "developability": aunque los appserver pueden hacer reload de ciertas cosas, no pueden hacer magia, y a veces pensar un poco en esta característica como parte de la calidad de la solución les va a ahorrar horas de frustración.

13 diciembre 2011

Configuración basada en Java con Spring 3.x

Si vienen siguiendo mi blog sabrán que además de postear una vez cada mil años, escribi algunas cosas sobre la dificultad de generar una configuración de Spring usando solo Java.

La buena noticia es que a partir de Spring 3.x se puede configurar el application context casi 100% en Java (hay una pequeña parte que todavia hay que hacer con el XML). Me entere de esto leyendo el anuncio del release 3.1 en InfoQ (y para mi sorpresa esta posibilidad ya existía en el release 3.0).

Si planean usar Spring 3.x es una buena noticia por que pueden tirar ese applicationContext.xml al tacho y hacerlo directamente en Java que es más fácil de mantener y refactorizar :)

La implementación se basa mucho en annotations, pero es lo suficientemente flexible como para permitir refactorings (para ejemplos ver la sección 3.11.3 del manual de Spring 3.0). Aunque hubiese preferido que lo hagan como en Grails, donde hicieron un builder usando Groovy (que dicho sea de paso, si están en un proyecto Java/Groovy pueden usar el builder sin necesidad de depender de otras librerías de Grails).

09 diciembre 2011

Mapeos Objeto/Relacional

Llegué a la conclusión de que los frameworks de mapeo Objeto/Relacional no sirven.

- Ehh! ¿Por que? Si yo uso Hibernate y esta bueno!

Aca van mis razones...

De Objetos...
Aprendí con el tiempo que la principal ventaja de la programación orientada a objetos es la posibilidad de tratar con distintas implementaciones a traves de su interfaz. 

Si estas pensando que me refiero al polimorfismo, tenes razón, pero di esta vuelta para evitar la tipica asociación de polimorfismo con jerarquias de clases.

Veamos un ejemplo que suele darse en los cursos introductorios: la cuenta bancaria.


Supongamos que tenemos en nuestro sistema un Cajero que puede hacer depositos en estas cuentas:



La instancia cuenta se pudo haber definido mediante una sub-clase, clase anonima, proxy dinamico o usando AspectJ y manipulación de bytecode. No importa. Mientras cumpla con la interfaz CuentaBancaria nuestro Cajero va a funcionar.

... y Relacional
Ahora supongamos que definimos una Cuenta de la siguiente manera:

CuentaBancaria cuenta = new Decorator(cuentaOriginal);

Nuestro Cajero sigue funcionando pero...

motorDeMapeoOR.guardar(cuenta);

explota.

El motor de mapeo O/R necesita saber como tratar la instancia para poder mapear su clase y sus variables de instancia a la base de datos. En otras palabras: encapsulamiento y polimorfismo las pelotas.

Quizas pienses que me fui a un ejemplo extremo y que uno nunca tiene casos como estos. 

Sin embargo en todos los sistemas JEE que veo siempre hay un "modelo" que termina estando sumamente acoplado a la base de datos:
  • No es conveniente jugar con las jerarquias de clases por que no se pueden mapear de una forma buena.
  • No se puede refactorizar algo para cambiar su composición por que es engorroso modificar los mapeos.
  • No se pueden usar objetos más "dinamicos", como objetos que tengan closures (en un lenguaje que lo permita), implemetaciones de interfaces definidas inline o simplemente un decorator como el del ejemplo.
Entonces la solución es: hacemos objetos pobres casi sin comportamiento solo para transferir datos (DTOs). Luego tenemos un modelo mas "rico" que se crea a partir de estos DTOs. 

Suena simple, pero en la práctica termina siendo una engorrosa y aburridisima repetición de código que los programadores tienden a solucionar de forma sencilla: en lugar de separar estos DTOs del modelo de dominio, terminan juntandolo en un modelo de dominio que solo guarda datos y muchas "clases utilitarias". Es decir aprovechan muy poco las posibilidades de polimorfimo que brinda el lenguaje. 

Pero si iba a estar todo tan acoplado y dependiente de la base de datos, ¿Para que nos preocupamos en hacer el mapeo? ¿No era mejor usar algo tipo Active Record? Al fin de cuentas son lo mismo que los DTOs pero con menos código.

Esto es lo que hace Rails y la primera vez que lo vi me parecio una cagada. Huele mal, pero los mapeos O/R no huelen mucho mejor.

...a las conclusiones
Los frameworks de mapeo O/R no cumplen con su promesa de un modelo de dominio libre de la base de datos, y me pregunto cuales son las ventajas en usarlos. Es decir si los usamos solo para facilitar la interacción con JDBC, quizás hay otras maneras que no requieran ningún tipo de mapeo y mantenimiento de "beans" intermedios.

En el caso de Java, creo que si Sun en lugar de definir una interfaz tan fea como la de RowSet que mezcla un montón de responsabilidades, hubiese hecho algo mas sencillo que se pueda usar como un Active Record, no habria razones para utilizar mapeos O/R como JPA.

09 noviembre 2011

Microwiki: Primeros pasos de diseño

No importa que hable sobre principios y patrones de diseño, sobre polimorfismo, o si el ejemplo que di fue bueno o malo; al momento de la practica quien recién comienza aprendiendo diseño orientado a objetos se siente perdido y quiere una especie de guía que le diga que esta bien o mal.

El secreto es que incluso con más experiencia, uno tambien se encuentra algo perdido cuando se empieza con un problema nuevo.

Creo que un buen consejo para encarar un diseño es tener una "mente de principiante" y no dejar de preguntarse "¿Por qué?".
Claro que para hacerse las preguntas y poder responderlas se necesita un conocimiento previo.
Ambas cosas -hacerse las preguntas y construir ese conocimiento- van de la mano.

Esta introducción, viene a cuento que me dieron ganas de contarles las preguntas que me fui haciendo en la construcción de microwiki.

Para quienes no hayan visto mi post anterior, microwiki, es un pequeño servidor wiki que comencé a programar en mis ratos libres. Como objetivos de diseño, microwiki tiene las siguientes características:
  • Se utiliza localmente: no hay usuarios, ni permisos, ni historial de versiones en las paginas. 
  • Iniciar el servidor debe ser tan simple como ejecutar un comando.
  • Las paginas se guardan en el file system, y pueden editarse tanto dentro como fuera de la aplicación web.
  • La búsqueda de contenido debe ser rápida.


Las cuestiones técnicas

En un mundo teórico ideal, uno debería evaluar la funcionalidad, los atributos de calidad, ver las herramientas disponibles, un largo etcéra y luego elegir la solución técnica más adecuada. La realidad es mucho más simple, venia jugando un poco con Groovy y Gradle así que esas fueron las herramientas que elegí para trabajar.

Al principio pensé en hacerlo con Scala para practicar un poco este lenguaje, pero la comodidad de IntelliJ IDEA para usar Groovy me compro -me estoy volviendo viejo, ya no tengo ganas de ponerme a configurar plugins en versión beta.

El resto de las opciones fueron más simples. Conocía la sintaxis Markdown de usar GitHub y Stackoverflow, y PegDown fue el primer parser que encontré para Java.

Y si voy a hacer un pequeño web server tampoco iba a empezar de cero, Jetty es muy conocido por proveer un API simple para crear web servers en Java sin meterse con todo el lío de JEE (otra opción en Grizzly, pero es mucho más nuevo y no tiene tanta documentación).


Primeros pasos

Partiendo de que microwiki es una aplicación web, y que voy a utilizar servlets con Jetty, el primer paso fue pensar en un servlet que mostrara una pagina.
Consejo: Empezar a diseñar siempre por un caso particular y simple.
Entonces tenemos nuestro servlet que muestra la pagina. ¿Implementamos en el servlet la funcionalidad de abrir el archivo e invocar al parser? Respuesta rápida: no.


¿Por qué? El servlet se encarga de manejar el request y response de http, si ponemos todo junto no hay forma de testear la funcionalidad de obtener y parsear una pagina por separado.

Probablemente en alguna clase sobre diseño orientado a objetos escuchaste que las clases deberían tener alta cohesión, se referían justamente a este tipo de casos: hacer que la clase servlet implemente dos funcionalidades distintas es contraproducente a la hora de introducir cambios.

Los tests de unidad son útiles para detectar este tipo de problemas: si dejamos todo junto para testear la responsabilidad de brindar una pagina vamos a tener que crear un mock HttpServletRequest y un mock HttpServletResponse.
Consejo: Los mock objects son útiles, pero si tus tests necesitan muchos, probablemente le estés pifiando en la separación de responsabilidades.
Entonces separando responsabilidades termine con algo así:


Para los "Templates" no hubo mucho análisis de mi parte: implementar la generación de HTML dentro del servlet es engorroso e inmantenible (en este caso la necesidad de separar responsabilidades es bien clara). Para implementar los templates use los GStrings de Groovy. Me pareció bueno mantener las cosas bien concretas: el template por ahora se utiliza para visualizar una pagina.
Nota: En el código actual en GitHub van a ver que el uso de los templates evoluciono hacia algo más generalizado, en otros posts les cuento el por que.
¿Por que Writable?
Writable es una interfaz de Groovy que simplemente describe el método "writeTo(Writer)". Podría haber usado String, pero usar Writable permite expresar solo lo que necesito y optimizar las cosas si fuese necesario.
Si te estas preguntando a que me refiero con "optimizar": si uno tiene una pagina grande es preferible hacer un "streaming" que guardar toda la pagina en un gran String. Lamentablemente el parser de Markdown que estoy usando no permite hacerlo, pero como están planteadas las cosas podría usar otro parser sin afectar al resto de la aplicación.

¿Por que un objeto Page y no retornar directamente el String con el contenido?
Esta claro que para mi sistema una pagina no es simplemente un String.
Un fanático de TDD y del "paso a paso" me diría que para el test de mostrar la página un String alcanza. Sin embargo sé que voy a querer modelar más cosas de una pagina: una pagina tiene un titulo, una representación HTML y una representación en formato wiki.

¿Por qué una interface PageProvider?
Bueno yo tambien tengo la misma duda :)
Por ahora solo tengo una implementación de PageProvider y tampoco tengo intenciones de tener una distinta a futuro. En este punto hay dos cuestiones basadas en la experiencia que me llevaron a esto:
  • Una interfaz PageProvider me facilitaría la creación de mocks en el caso que quisiera testear otros componentes que dependan de un PageProvider (si esta es una de las "malas" costumbres adquiridas de la experiencia en Java).
  • Si quisiera agregar un cache podría usar un decorator que implemente esta interfaz.
    Otra vez me estoy adelantando -son las manias que uno adquiere de la experiencia previa- en estos casos es importante tomar nota mental de que uno se esta adelantando. A veces por adelantarse, uno le puede errar fiero (de hecho me paso con la búsqueda, pero eso se los voy a contar en otro post). En este caso decidí seguir adelante: le veo mas ventajas que contras, pero si les pasa algo asi en un diseño y les queda la duda... paso a paso como diría mostaza.

Espero no haberlos aburrido mucho, la próxima les cuento algunos pasos más.

17 octubre 2011

Microwiki

El feriado lluvioso de la semana pasada, lo dedique a un pequeño proyecto que tenia en mente hace tiempo: hacer una pequeña wiki.

¿Para que sirve? Sirve para tener un wiki sin necesidad de un servidor externo, guardando los archivos en forma de texto plano. La ventaja es que la documentación del proyecto se mantiene en una carpeta junto con el código, en una forma fácil de editar y buscar. El formato que usa es Markdown, que es lo suficientemente amigable y legible para mantener los archivos de texto sin necesidad del wiki.

Pueden encontrar el código en GitHub: https://github.com/dfernandez79/microwiki

Todavía esta en desarrollo, pero la funcionalidad básica de ver y editar esta disponible. Hay muchas cosas por hacer que voy a ir agregando cuando tenga tiempo, al menos la siguientes características van a estar terminadas para el "release 1.0":
  • Búsqueda de texto rápida (quizás usando Lucene). Mi objetivo es que browsear y buscar sea rápido, de lo contrario no le veo mucha ventaja sobre utilizar archivos Word u HTML :)
  • Paquete binario fácil de usar, la idea es que el típico caso de uso sea hacer checkout de los archivos fuentes e iniciar el wiki (ya sea por un comando o un icono) con cero configuración (por default voy a hacer que tome el subdirectorio docs del directorio actual).
  • Modo solo lectura (lo veo útil para integrar con Hudson u algún otro servidor de CI)
Otras cosas que me gustaría agregar a futuro son:
  • Soporte para LaTeX probablemente usando http://www.mathjax.org/.
  • Soporte para renderizar grafos de GraphViz directamente (la idea seria que los links a gráficos usando la notación ![Alt text](file.dot) de Markdown se muestren directamente como PNG).
Si tienen interés en chusmear el código fuente, las herramientas que use son:

12 octubre 2011

Un brevísimo vistazo a Dart

El lunes pasado - 10/10/2011- Google presentó un nuevo lenguaje de programación llamado Dart.

La presentación realizada por Gilad Bracha y Lars Bak había sido anunciada hace unos meses atrás.

Dado que estos dos personajes hicieron cosas interesantes y tienen un pasado común en Smalltalk, el anuncio me genero mucha expectativa.

Después de leer un poco el sitio (por ahora solo el código fuente esta disponible para el publico general), mis expectativas decayeron: al menos en la superficie Dart no es muy distinto a lo que vienen produciendo otros lenguajes como Scala o Groovy.

Debido al trabajo de Gilad Bracha en Newspeak, y a que JavaScript es un lenguaje basado en prototipos, yo esperaba un lenguaje estilo Newspeak pero con sintaxis ala JavaScript. (En este video G. Bracha habla justamente que Dart es un lenguaje más "conservador" en comparación con Newspeak)

Tampoco voy a ser tan negativo, el gran punto a favor que tiene Dart es que si Google decide promocionarlo puede hacerlo a través the Chrome y Android. Y si logra que Mozilla lo adopte puede lograr algo similar a lo que ocurrió con HTML5: que IE tambien lo adopte como standard.

Lo más interesante es que si las herramientas que provee el lenguaje son las adecuadas e incursionan en herramientas del lado servidor, pueden lograr un lenguaje de programación ubicuo. Hoy en día JavaScript con node.js lo es, aunque las limitaciones del lenguaje respecto del manejo de módulos y la disponibilidad de herramientas no lo hacen para mi una opción fuerte del lado servidor.

Según lo que pude leer del material disponible, Dart es todavía un "work in-progress", y no se definieron ciertas cosas importantes sobre como va a ser el manejo de modularidad. En resumen algunas características de este lenguaje son:
  • Se va a poder utilizar en el browser, directamente si es Chrome (al menos en el futuro), o compilando JavaScript si no (como CoffeScript).
  • Es un lenguaje con clasificación con tipado opcional (como Strongtalk o Groovy).
  • Al igual que en muchos lenguajes dinámicos, se pueden re-definir operadores y tiene algo de syntax sugar como strings con variables del estilo "Hola $mundo"
  • Tiene generics y están reificados (los generics pueden ser opcionales como en Java y la información se mantiene "en runtime" como en C#).
Hasta ahora nada mal pero tampoco demasiado novedoso, lo que si destaco es:
  • Tiene un modelo de actores (como Erlang), llamado Isolates. Me parece una forma buena de encarar concurrencia.
  • Un programa compilado se puede guardar en un "snapshot" que es como una imagen de Smalltalk (de hecho esta idea viene de Strongtalk, donde creo tambien se llama snapshot). Eso es interesante por que si logran agregar el soporte adecuado en los browers, quizás logren hacer lo que Java no pudo lograr: un lenguaje con bytecode multiplataforma que funcione directamente en todos los browsers.

Lo que no me gusto de lo que leí hasta ahora es:
  • No vi absolutamente nada relacionado al manejo de "módulos" (de hecho ni siquiera existe el concepto de namespace o paquete), eso puede ser bueno: Gilad Bracha escribió artículos muy interesantes sobre este tema en su blog, asi que puede significar que todavía están evaluando la mejor forma de hacerlo. O bien puede ser malo, en los ejemplos vi unos #import  que funcionan como un include C que huelen muy mal.
  • Factories e interfaces. No sé si poner esto entre las cosas buenas o malas. Se me ocurren varios casos donde puede ser bueno: refactoring de una clase concreta a una interfaz, usos de implementaciones comunes por default (ej. en Java: HashSet o ArrayList). Sin embargo tienen un lado malo: funciona como un mecanismo rudimentario para manejo de dependencias estilo "service locator", algo que en Java demostró ser muy problemático.
  • Uso de underscore para indicar que algo es privado (Lang Spec sección 3.2). Es un shortcut aceptable para las variables de instancia, pero para los métodos me parece demasiado que quienes lo invocan sepan si lo que usan es privado o publico.
  • Las librerías "core" son una copia de las de Java, hubiese estado bueno que cambien algunas cosas como por ejemplo tener listas mutables e inmutables (ala Scala).
  • No vi que los generics se puedan acotar, por ejemplo HashSet<E> no es un HashSet<E extends Hashable> (Object en Dart no implementa hashCode, hay una interfaz Hashable).
En fin veremos que depara el futuro, es difícil hacer predicciones en estas cosas, pero creo que no es necesario que el lenguaje sea bueno para que tenga éxito si no que la plataforma lo sea. Si Google decide usar Dart para hacer aplicaciones Android nativas, y programar en el web browser, entonces puede que se convierta en el sucesor de Java. Sin embargo no creo que eso pase, desde afuera parece que la estrategia de Google es apostar a muchas cosas para ver que queda.

27 septiembre 2011

Tip rápido para los que usan Mac

Si usan MacOSX y tienen Growl, les recomiendo instalar el extra de growlnotify (esta en la carpeta Extras de la imagen de instalación de Growl).

Es muy util para notificar cuando termina una tarea de esas que llevan tiempo como un build o un update, por ejemplo:

svn update && growlnotify -m "Termino el update"
mvn clean install && growlnotify -m "Termino el build"

14 septiembre 2011

Herencia en JavaScript

JavaScript es en esencia un lenguaje basado en prototipos, que sufre de personalidades múltiples: tiene algunos operadores y comportamientos de un lenguaje con clasificación.

Para entender esto olvidemos por un momento a JavaScript.

Un lenguaje orientado a prototipos -como por ejemplo Self- se basa en que uno obtiene nuevas instancias a partir de la clonación de otras, por ejemplo en un "pseudo-JavaScript":

var auto = {
     acelerarACienEnUnSegundo: function() { return 'No puedo'; },
     arrancar: function() { return 'brummm... :)' }
};

var ferrari = clonar(auto);
ferrari.acelerarACienEnUnSegundo = function() { return 'fiuuummm'; };
ferrari.arrancar(); // retorna brummm....

¿Donde esta la esquizofrenia de JavaScript? Esta en que contamos con un operador new que simula comportarse como si estuviésemos trabajando con un lenguaje con clasificación, por ejemplo:

var auto = new Auto();
function Auto() {
     this.arrancar = function() { return 'brummm....'; };
}

¿Qué hace new?
Cada función que uno define en JavaScript es un objeto que tiene asociada una propiedad prototype.
Es como si cuando uno escribe function Auto() {} escribiera:

var Auto = {
    prototype: {}, 
    constructor: function() { /* cuerpo de la función Auto */ } };

Cuando se usa el operador new JavaScript lo que hace es:

  • Clonar el objeto prototype
  • Después evaluar la función en constructor usando la nueva instancia como contexto (es decir this es el clon de prototype)

Es por eso que uno podría escribir la función Auto como:

function Auto() { console.debug(this.arrancar); }
Auto.prototype.arrancarfunction() { return 'brummm....'; }; 

Esto es muy confuso, por que pareciera que una función se puede comportar como si fuese una clase. De hecho genera más confusión aún cuando uno quiere compartir implementación usando algo similar a la herencia.

Sin embargo el problema es más simple de resolver: no hay clases ni tipos, solo tenemos que agregar los métodos que necesitamos compartir, por ejemplo:

var agregarComportamientoDeAuto = function(obj) {
      obj.arrancar =  function() { return 'brummm... :)' };
}

¿Como creamos un objeto que se comporte como un auto?

var ferrari = function() {
      var nuevaInstancia = {};
      agregarComportamientoDeAuto(nuevaInstancia);
      nuevaInstancia.acelerarACienEnUnSegundo = function() { return 'fiuuummm'; };
};

Quizás no luzca muy elegante, pero en mi opinión es mejor que simular un lenguaje con clasificación donde no lo hay.
La ventaja de este método es que es muy fácil crear mixins o tener variables privadas (en el ejemplo podríamos pasar argumentos adicionales a agregarComportamientoDeAuto y aprovechar las clausuras).

También es posible simular delegación estilo "super", la próxima voy a dar un par de ejemplos de eso, pero adelanto que es una solución muy poco elegante.

02 septiembre 2011

Some Java "dependency injection" bad practices

When Spring and other dependency injection frameworks appeared on the Java space, some of its goals where to be "non-invasive" and to facilitate unit testing (see this old Rod Johnson presentation).

But when DI frameworks started to take advantage of annotations introduced in Java 5, the term "non-invasive" became relative, why? Here are some reasons:

@Autowire and @Inject annotation in private fields/private constructors

Spring, CDI and Guice allows you to annotate private fields, to let the DI framework inject the appropriate instance. But this feature has two problematic consequences, lets see them in a example taken from the Jboss CDI documentation:

public class Checkout {
       private @Inject ShoppingCart cart;
}


Consequences:

  1. Is impossible to create a valid Checkout instance without using a framework that supports the @Inject annotation. 
  2. You would not see the dependency directly by looking into the Checkout public interface or JavaDocs.
(1) is bad because your code becomes more coupled with the DI framework. For example if you want to create an unit test that uses a Checkout instance, you have to set up the DI framework first.

There are annotations and test classes to make tests for Spring beans, but in my experience (after having to maintain application-context.xml files created and used by multiple tests) is an unnecessary complexity that could be removed if the code is not coupled with the DI framework.

Some people argue that (2) is good (for example: this Guice "best practice"). The idea behind that is that developers will do "bad things" if they can: instead of obtaining the required instance by using dependency injection, they will use the constructor.
But that has more to do with a communication problem, and should be resolved by using code reviews, pair programming and walkthroughs.

@Required
In the same tone as the @Inject for private fields comes the @Required annotation supported by Spring. That annotation allows you to write things like:

public class Shape {
       private Color color;
       @Required
       public void setColor(Color c) {
            color = c;
       }
       ....
}

Then the DI framework will give you an exception if the dependency is missing.

But what happens if you instantiate a Shape outside the DI framework? You don't know if you have to set the color or not.

A constructor is simple: no dependencies with the DI framework and comes free with the Java language :) (This part from the "famous" Martin Fowler article on DI, also talks about constructors and setters in DI frameworks)

Note for the reader: I wrote the last articles in English because I wanted to practice that language. But I think that is better to keep the blog posts in one language, and Spanish is my native language, that's why this will be my last post in English (at least in this blog).



26 julio 2011

Using embedded databases in unit tests

Two years ago, I wrote a long blog post -in Spanish- about Test Driven Development. One of the sections of that blog post was about testing code that depends on a database. In short, never use an external database for unit tests.

Using mock objects to simulate the database connection is impractical. A better option is to start and shutdown a embed database. For that matter pure Java SQL databases like HSQL DB are really lightweight, for example a unit test that reads resources from disk usually takes more time than one that uses HSQL in memory.

At the time of that blog post I wrote a small code snippet to start and shutdown HSQL. I used that code snippet many times, but never took the time to create a reusable library for it. Fortunately the Spring guys made it for me :)

Since Spring Jdbc 3 there is an interface to handle embed databases for testing.

If you use Maven, add the dependency to Spring Jdbc (you don't have to use the whole Spring framework if you don't want to) and to HSQLDB:

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>3.0.5.RELEASE</version>
     <scope>test</scope>
</dependency>
<dependency>
     <groupId>hsqldb</groupId>

     <artifactId>hsqldb</artifactId>
     <version>1.8.0.7</version>
     <scope>test</scope>
</dependency>

Start the embed database before your tests:


@BeforeClass
public static void setUpDatabase() {
     sharedDatabase = new EmbeddedDatabaseBuilder() 

                           .addScript("hsqldb-test-ddl.sql")
                           .build()
}

The EmbedDatabase implements DataSource, so you can pass it to your objects:

hibernateSessionFactory.openSession(sharedDatabase.getConnection());

Shutdown the database after the tests:

@AfterClass
public static void tearDownDatabase() {
    if (sharedDatabase != null) {
        sharedDatabase.shutdown();
    }
}


Usually starting the HSQL server is fast, so you can use @Before and @After (instead of @BeforeClass / @AfterClass).

That's all.

02 mayo 2011

Using Spring dependency injection without XML

This is my second post in English, which is not my first language, so if you find errors let me know :)
Having said that, let’s go with the blog post…

It’s difficult to define what the Spring Framework provides, because is not a framework but a collection of them. The included frameworks covers pretty much everything from dependency injection (DI) to the creation of web applications. All of them designed to work seamlessly with the DI framework, and in particular with the XML way of configuring DI.

By using dependency injection you don’t need to modify your model to use Spring: you depend only on the DI framework to get the root objects of your application. At “lower levels” you can use some abstractions provided by the Spring Framework like JdbcTemplate or JmsTemplate, but you can use them without the DI framework too: they are part of the “Spring framework collection”, but they can work as independent frameworks.

Sadly in my experience, it seems that most of the developers are not aware of that: a lot of projects are too coupled with the Spring XMLs (which is only one convenient way of configuring DI), to the point that there are also dependencies to the “application-context.xml” in the unit tests. This is really bad. Spring XML is easy to write, but is a pain to maintain: it lacks abstraction mechanisms, you can’t debug it, is difficult to refactor and to find where the things are.

An alternative is to use Google Guice for the dependency injection part: It contains a nice API to define instance dependencies in code. You can use it with other Spring things, but I never feel comfortable to do that, because I think that mixing both frameworks could be a little bit confusing.

Spring DI can be configured by code, but to do it you have to pass some barriers:

  • All the documentation refers to the XML configuration.
  • The API to do the configuration directly in Java is awful.

 

Understanding the Spring API

The base interface to get instances from the DI injection framework is BeanFactory. The “Bean” prefix is there for historical reasons, but not be confused by it: your object classes doesn’t need to comply with the JavaBeans specification.

But to use a BeanFactory instance you need to configure it in some way, that’s why exists other interface called BeanDefinitionRegistry. This interface defines methods to register bean definitions (instances of BeanDefinition) that indicates how to create an instance.

To summarize:

  • BeanFactory: provides bean instances by name (getBean)
  • BeanDefintionRegistry: associates bean names with bean definitions (registerBeanDefinition).
  • BeanDefinition: provides information on how to create an object instance.

    Design side note: To me Spring is unnecessary complex in this part. The BeanDefinition provides information but it doesn’t do the instantiation, it means that each BeanFactory has to do that work (see AbstractBeanFactory.doGetBean). I think that if Spring delegates the instantiation to the definition, a lot of things could be simplified and the API becomes easier to use directly from Java.

If you used Spring before, you probably heard the name “ApplicationContext”. This is because the usual ways to get an implementation of BeanFactory is by using a ClassPathXmlApplicationContext or a XmlWebApplicationContext. That classes also implements other interfaces (i.e. ApplicationContext) that allows to plug-in lifecycle observers or enumerate beans (I assume that those functionality is required by other more “advanced” features provided by the framework).

 

Creating a BeanFactory instance for testing purposes

Before explaining how to do that… I want to give you an advice:

If you need to pass a Spring “ApplicationContext” or a “BeanFactory” in a lot of tests, you need to review your design and/or the way that you are testing. Maybe you are putting direct dependencies with the Spring context when all you need is to pass the required instances directly or maybe you are instantiating too much just to create an unit test.

After that short advice, here are some small code examples:

You can define beans by code, sadly the API is really ugly: BeanDefinitionBuilder provides you with methods to define beans (Ey, Spring guys! A good internal DSL to do this will be nice in the next release of Spring):

final StaticApplicationContext context = new StaticApplicationContext();
final BeanDefinitionBuilder builder =
       
BeanDefinitionBuilder.genericBeanDefinition(MockDataSource.class);
context.registerBeanDefinition("dataSource", builder.getBeanDefinition());

new MyApplication(context).run(); // MyApplication(BeanFactory context)

Using StaticListableBeanFactory you can pass instances, instead of using bean definitions (really useful to do tests):

final StaticListableBeanFactory context = new StaticListableBeanFactory();
context.addBean("dataSource", new MockDataSource());

new MyApplication(context).run();

By using a GenericApplicationContext you can combine XML from different sources with beans defined in code, for example:

GenericApplicationContext context = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
// this xml define the contentManager bean
reader.loadBeanDefinitions(new ClassPathResource("spring-config.xml"));

context.registerBeanDefinition("dataSource",
        BeanDefinitionBuilder.genericBeanDefinition(MockDataSource.class)
                             .getBeanDefinition());
context.refresh();

// contentManager uses dataSource
System.out.println(context.getBean("contentManager"));

 

Ok that’s all for this time :)

18 abril 2011

Java coding best practices

For a software project in which I worked as a external code/design reviewer, a contractor asked me to do a document with Java coding best practices.

Instead of giving them a big document that nobody is going to read, I preferred to make a small list of good practices - and try to fit them in a A4 page.

I'm not a big fan of "best practices" documents. I know that they are good to standardize the work and share knowledge. But there is a "dark side": a "best practice" depends on a particular context. So is good to understand why a practice was good and distinguish when there are better options; in the end you can't get blood out of a stone: the best way to produce good quality systems is to work with good professionals.

So this is the list of best practices that I made (in no particular order):

Choose Meaningful Names
- Classes:
     If a class represents a concept of the problem domain, use a well known name for that domain (the technical analyst/product owner is our friend here).
     Forget the name, look at the class responsibilities and them choose a name based on that (CRC helps).
     Using suffixes like "Data", "Info", "Object"; doesn't add information and can be confusing ie. What is the difference between Customer and CustomerInfo? 
- If you still don't known how to name something and you invent a new name: communicate that to your team, and be sure that everybody agrees to name "the thing" in the same way. 
- When you start to feel uncomfortable with a method or class name and you have a better name, don't heistate: rename it.
Write short methods
- Remember the "Extract method" refactoring shortcut keys, you are going to use them a lot. 
- Methods from comments: usualy in a long method programmers start to use comments to explain parts of the code, create a new method instead of using comments in that way. 
- If you can't do an extract method because you end with lots of  parameters or you have some obscure dependency; is a sign that something is wrong with the design. Sometimes using a "Method Object" to fix that helps to find what is missing.
Avoid undesired side-effects (use immutable objects as much as possible)
- Avoid mutable global state, ie: non-final static variables or "singleton" like globals (see "Avoid Singletons"). 
- Try to use constructors with arguments instead of a combination of "default constructor/setters" (this is related with "Avoid object instances in invalid state" and "Avoid nulls")- 
- Be careful when returning collections: most of the Java collections are mutable, returning those could expose your object internal state.
Avoid object instances in an "invalid state"
- Declare required instance variables as final, and initialize them in the constructor. 
Use good defaults. 
- If you end with a constructor with lots of parameters: refactor the code, maybe you can group of those parameters in an object.
- If you need to build a complex instance, you can create a builder (but never put the validations in the builder, put them in the constructor).
Avoid nulls
- You can use the Null Object pattern 
- Use good defaults instead of null
Use singletons ONLY to represent unique objects
- A lot of programmers use the singleton pattern to point to globally well known objects, this combined with mutable objects is a time bomb: lots of undesired side effects and code that could have concurrency problems. 
- If you need a "setInstance" method for testing purposes: is not a singleton! 
- Usually "Null Objects" can be singletons, but most of the time singletons are not needed. 
- Singletons should be immutable.
Classes should have one responsibility - one reason to change
- Using the CRC method sometimes helps to think in single responsibility classes. 
- Classes with many responsibilities tend to be difficult to test with unit tests; so a hard to write unit test could be a sign that you are dealing with too many responsibilities.
Write unit tests like specifications
- In TDD the most confusing part of unit test is the word "test". If you think in tests like in small specifications of that your code supposes to do, then is very easy to write tests first.
- Don't put to many assertions in one test method, is better to have only one or two assertions per unit test method. Why? You'll start working faster with the TDD cycle and if something fails is easy to see what's happening.
Use exceptions instead of return codes
- If you "Avoid Nulls" you will find this straight forward.
- Think in exceptions in a layered way, and handle them in the place that you could do something about it.
- Don't hide exceptions with empty catch blocks: if you have a bug it will be very difficult to find.
- Never catch Throwable: because you can catch sub-classes of Error too.
When creating polymorphic class hierarchies: remember the substitution principle
- If you have a method that is overridden by different subclasses: avoid coupling between the type of the method argument and specific subclasses.
- If you need to cast an argument, maybe you are not following the substitution principle, take account that even if the "subtypes" are ok to the compiler you will not be able to change implementations easily.  
Optimize only if it's necessary and with the help of a profiler
- Applying optimization tips blindly... tend to create wrong assumptions, and sometimes code that is difficult to understand.
- A linear search in a collection with a bounded number of elements is O(1) (even when the max number of elements is 100, 1000 or 10000)
- I/O is orders of magnitude more expensive than CPU/Memory tasks.
- Generational Garbage Collectors like the ones in Java are optimized for short lived objects, it means that if you need to create objects to write more understandable code: create them. Early optimizations in object instance creation could cause other problems in concurrency and impact in the GC time; optimize object creation ONLY if you conclude that is necessary after using a profiler.

09 abril 2011

Juego de la vida (2da parte: implementación)


Hecha la introducción sobre el juego de la vida de Conway en el post anterior, paso a contarles algunos detalles de implementación.
No voy a referirme al algoritmo: es el primero que me salió,  en cambio vamos a ver algunas cosas interesantes para aprender de JavaScript y HTML5.


Doctype 
La primer duda que me surgió a la hora de escribir el HTML5, es ¿Qué DOCTYPE uso?
Quienes recién comienzan con HTML quizás no entiendan esta duda, es una larga historia resumida de forma excelente en "Dive into Html 5": http://diveintohtml5.org/semantics.html#the-doctype .

En pocas palabras, el DOCTYPE que debe usarse en HTML5 es simplemente: <!DOCTYPE html >


Progressive Enhancement
Progressive enhancement es una técnica que consiste comenzar con el contenido, usando los tags de HTML para estructurarlo (títulos, párrafos, listas, etc) y luego ir agregando el diseño y "comportamiento" mediante CSS y JavaScript.

Pueden encontrar una buena introducción en este articulo.

Claro que en la pequeña aplicación JavaScript no hay contenido :), en fin el objetivo era aprender y practicar técnicas útiles para el desarrollo web.


CSS
Los stylesheets son sumamente engorrosos de mantener. Afortunadamente existen lenguajes de macros que permiten generar stylesheets usando constantes y funciones.
Uno de ellos es LESS, que lo elegí por dos razones: pueden usar JavaScript en el cliente para hacer pruebas y para aplicaciones Java cuentan con un servlet.
Otra alternativa para quienes usan Ruby es Sass.


Canvas y gráficos dinámicos
Para los gráficos comence usando un Canvas.
Lamentablemente el API de Canvas es en cierto sentido de "bajo" nivel: provee primitivas para dibujar directamente, pero carece de algunas abstracciones convenientes.
Luego de frustrarme (por alguna razón que no pude encontrar, los gradients en el stylesheet que uso modifican el estilo de dibujo del Canvas... algo muy engorrozo que me paso tanto Chrome como en Firefox) decidí buscar algun framework JavaScript que me facilite las cosas.
Entre los frameworks que estube mirando me encontré a gusto con raphaeljs.
No usa Canvas si no SVG (que al parecer no esta disponible en algunos browsers para smartphones).
Pero el API me pareció simple e ideal para hacer visualizaciones en HTML.


Local storage
Guardar y retornar información local en HTML5 es extremadamente fácil:

localStorage.setItem("key", "hello");
var value = localStorage.getItem("key");

Claro que este "map" es bastante rudimentario, hay otras opciones pero funcionan solo en ciertos browsers.
Aunque no lo use en este ejemplo, también pueden proveerle al browser información de que archivos mantener localmente para que la aplicación funcione offline.


Debugging y profilling
Con la ayuda de Diego Camera, conoci algunas herramientas de debugging y profiling de Google Chrome.
Tanto si usan Chrome o no, cuentan con la extensión FireBug que les agrega un API de consola extremadamente útil para hacer debugging y profiling (en Chrome no necesitan instalar una extensión para acceder a este API).

Para destacar: console.dir(object) les permite inspeccionar rápidamente el objeto pasado como parámetro.

Una cosa que me gusta de trabajar con estas herramientas es que me dio casi la misma sensación que en Smalltalk: uno puede inspeccionar y modificar en "entorno" en cualquier momento. Quizás en algún momento con proyectos como Ace (antes Mozilla Bespin/Skywriter) y  Eclipse Orion se llegue a lo que hacían los entornos de Smalltalk hace más de 20años ;-)

Para terminar....
Este post solo tiene algunas "puntas" a mirar para aprender más sobre JavaScript y HTML5.

Si están interesados en hacer algo para aprender, la implementación del juego de la vida que presente en el post anterior tiene muchas cosas pendientes, y con distintos niveles de complejidad:
  • Cada celda tiene su propio event handler, se podría usar un unico event handler para ahorrar recursos.
  • Las celdas se pintan haciendo click, lo cual es bastante incomodo si quieren pintar más de una celda. Se podrían cambiar los event handlers para permitirlo.
  • La función de "Save/Load" solo permite grabar una instancia, se podria modificar para soportar mas de una.
  • Seria útil poder insertar patrones de celdas predefinidos
  • Se puede cambiar la implementación para usar el tag canvas en lugar de raphaeljs
  • ... y si son unos "grosos" de los algoritmos, podrían simular que el "mundo" es infinito en lugar de hacer la simplificación que hice

05 marzo 2011

Juego de la vida (1ra parte)


Hace poco, conocí a través del libro "El gran diseño" de Stephen Hawking, el "juego de la vida" de Conway.

Este "juego" -entre comillas- me resultaba conocido, ya que en alguna vieja distribución de Linux, había visto algo similar. Pero hasta hace unos días no tenia ni la menor idea de que se trataba.

Pero basta de misterios, les cuento muy brevemente de que trata.

La idea es que uno tiene una gran grilla con celdas. Las celdas tienen una configuración inicial: vivas (activadas) o muertas (desactivadas). A partir de esta configuración inicial se deja correr el tiempo -que son pasos discretos. A cada paso se determina que celdas nacen o mueren según unas reglas muy simples:


  • Si la celda esta viva pero no tiene vecinos: , en la siguiente generación muere:   
  • Si la celda esta viva pero tiene entre 2 o 3 vecinos: , en la siguiente generación permanece viva: 
  • Si la celda esta muerta pero tiene 3 vecinos: , en la siguiente generación se transforma en una celda viva: 
  • Si la celda tiene más de 3 vecinos, muere.

Notar que cuando se evalúa el estado de las celdas, es como si se sacara una foto: las reglas se evalúan en base al estado de esa foto.

Hasta acá nada interesante, ¿Donde esta la gracia de todo esto?
La gracia esta en que dependiendo de la configuración inicial, si uno deja correr el "tiempo" las celdas cobran vida. Hasta el punto que es posible simular una maquina de Turing!

El aprendizaje de todo esto, es que "formas de vida" muy simples pueden dar lugar a comportamientos muy complejos. Algo que menciona Douglas Hofstadter en su libro Gödel-Escher-Bach cuando hace la analogía entre las neuronas y las colonias de hormigas.

Como ven esto es algo muy interesante y simple de implementar a la vez :)
Por eso lo use como excusa para hacer algunos experimentos con JavaScript, experimento que pueden bajar de este link.

Nota:
El link contiene un WAR (Java Web Application Archive) que no es más que un archivo .zip.
Pero no necesitan Java, ni Tomcat o Jetty para probarla.
La razón por la que use un WAR es la siguiente: quisé hacer tambien el experimento de usar LESS (un lenguaje de macros para CSS -altamente recomendable).
LESS incluye una version JavaScript que pueden usar en el cliente... pero desafortunadamente usa un XMLHttpRequest para bajar el CSS, y por razones de seguridad browsers como Chrome lo filtran.
Por eso termine haciendo una aplicación web, que usa un servlet para LESS (tenia otras opciones, pero quería probar LESS y esta opción era más sencilla que instalar Node.js en Windows).
El uso de LESS no es necesario, pueden jugar con el ejemplo sin necesidad de levantar una aplicación web: abran directamente el archivo index.html dentro del war.
En el próximo post les cuento como desarrolle esta pequeña implementación (que usa jQuery, Raphael y local storage).

Mientras tanto les recomiendo jugar con patrones como este (pueden usar la opción de Save/Load para guardar/recuperar el patrón del local storage):


23 febrero 2011

Reingeniería de software (2da parte)

En el post anterior presente los términos utilizados comúnmente en reingeniería de software. En este voy a centrarme en una herramienta para design recovery -desarrollada por el Software Composition Group de la universidad de Berna- llamada Moose.

Moose esta compuesto de varios frameworks, de los cuales voy a mencionar los que creo principales para la tarea de design recovery:


  • FAMIX*: es un modelo que representa entidades de código y sus relaciones (para su posterior análisis estático). * En la documentación y papers que lei sobre Moose se me hizo un tanto confusa la diferencia entre FAME y FAMIX. Por lo que pude entender FAMIX es el nombre metamodelo y FAME la implementación en Moose para el intercambio de estos meta modelos.
  • Moose Core: trabaja sobre el meta modelo de FAMIX permitiendo hacer consultas usando Smalltalk.
  • Mondrian: es un framework de visualización, que facilita la creación de vistas polimetricas (vistas polimetricas? más a continuación).


Hasta acá todo muy lindo con la lista de herramientas, pero... ¿Cómo me ayudan a entender un sistema?

Cuando vi Moose por primera vez me hice la misma pregunta. En realidad, como no estaba familiarizado con las herramientas de design recovery mi pregunta fue mucho más básica: ¿Para que sirve Moose?

Si bien hay un libro online (estilo wiki), no me quedaba claro como se usaba la herramienta hasta que leí la tesis de doctorado de Michele Lanza (es especial el capitulo 4).
Para quienes quieran buscar ejemplos de como usar las herramientas de Moose, les recomiendo leer esta tesis. Y para los que no, les cuento brevemente de que trata.

Como mencione en el post anterior el problema principal de la reingeniería no es hacer el rediseño (o refactoring), si no entender lo que hay. Y como los sistemas suelen ser enormes, es necesario generar y visualizar de alguna manera información de resumen que ayude a entender las cosas a alto nivel, o al menos guiarnos hacia los lugares de importancia.

Con este objetivo Lanza trabajo en un tipo de visualización llamado vistas polimetricas que mezcla entidades de software y sus relaciones, junto con métricas de software. (en este articulo o en el libro Object-Oriented Metrics in Practice pueden encontrar una explicación detallada de las vistas polimetricas).

La idea de mezclar métricas en la visualización, es la de poder identificar visualmente posibles candidatos a mirar. Acá van algunos ejemplos:

System Hotspots View
Da una idea general del tamaño del sistema. En este gráfico cada rectángulo representa una clase, y su tamaño esta dado por el número de métodos. (Los cuadrados grises son meta clases, esto se hizo sobre un sistema en Smalltalk, en general tener muchos métodos en la meta clase es un indicador que están faltando abstracciones adecuadas -es casi como tener muchos métodos static en Java):



System Complexity View
Muestra las relaciones de herencia entre clases y los tamaños de cada una de ellas (ancho: número de atributos, alto: número de métodos).


Inheritance Classification View
Las dos vistas anteriores fueron un ejemplo de lo que Lanza llama "first contact" views, es decir sirven para tener un pantallazo muy general, en cambio esta vista se aplica a una jerarquía de clases.
La forma de árbol muestra relaciones de herencia, el ancho de las cajas la cantidad de métodos agregados, el alto la cantidad de métodos sobre-escritos y el color (en escala de grises) la cantidad de métodos sobre-escritos que llaman a super.


Estos fueron algunos ejemplos, hay más vistas utilizadas para obtener información a distintos niveles de detalle. Cabe aclarar que la idea es que estas herramientas de visualización sean interactivas (ej. pasar el mouse sobre cada caja nos dice la clase), de nada sirve tener cajas de colores si no sabemos de que parte del sistema nos esta hablando.

¿Donde entra Moose en todo esto? Moose es un framework que permite crear estas vistas y explotar la información. "Out of the box" no es una herramienta automática para hacer una evaluación del sistema, si no más bien un framework, que se puede automatizar (usando Smalltalk) e integrar a sistemas de integración continúa (como Hudson o mejor dicho Jenkins).
Nota: Moose esta hecho en Smalltalk, pero puede analizar sistemas en Java usando un conversor de Java al formato FAMIX.
¿Existen otras herramientas de este estilo?
Mas o menos. Moose es un framework general, lo más parecido a un framework general de este tipo es una vieja herramienta llamada Rigi (que ya parece estar obsoleta - al menos no se actualiza desde hace mucho).

Si existen sistemas que proveen visualizaciones particulares, por ejemplo CodeCity es un software muy interesante que lee modelos de FAMIX y realiza vistas basadas en métricas pero utilizando una metáfora de ciudad en 3d, por ejemplo la siguiente es una representación del JDK (versión 1.5) realizada con CodeCity
Si mal no recuerdo: alto=número de métodos, ancho=número de atributos, los colores varían según lo que se quiera ver por ejemplo se pueden utilizar para resaltar "design disharmonies" -otro término para "code smells". Las "manzanas" de la ciudad representan paquetes.




08 enero 2011

Reingeniería de software (1era parte)

Diseñar un sistema desde cero es una actividad creativa y entretenida. Elegimos la tecnología más adecuada, pensamos un diseño que cumpla con las necesidades e intentamos hacer las cosas lo mejor posible.

Luego: nuevos requisitos, tiempos ajustados, parches en el diseño, nuevos desarrolladores, más funcionalidad,  interacción con otros sistemas y tecnologías; más parches, ese framework que era novedoso ya es obsoleto y problemático; cambios de presupuesto, rotación del equipo...

Y así, nuestro hermoso “bebe” devino en una bestia inmanejable.
Hombre lobo o bola de lodo, no importa, cambiamos de trabajo/proyecto y ahora empezamos de nuevo.
Ilusos -todo vuelve- ahora cae a nuestras manos una bestia.

Primer impulso: dinamitemos la cosa y empecemos de cero.
No es mala idea, hay quienes estiman que el costo de mantenimiento va del 50% al 75% del costo total de un sistema de software (Davis, Sommervielle).
El problema es que el costo de desarrollo no es la única variable a tener en cuenta: desde el punto de vista de los negocios comenzar de nuevo puede ser una maniobra de mucho riesgo.

¿Por donde empezar?
Bienvenidos a la reingeniería de software.
Para adentrarnos en este tema vamos a comenzar por ponerle nombre a algunas cosas que probablemente ya sabemos -una tarea muy aburrida pero útil a la hora de buscar y compartir información.

La mayoría de los investigadores en este área utilizan los términos descriptos en el articulo de Chikofsky y Cross y en el paper de Kazman, Woods y Carriere:


Reengineering. Reengineering, also know as both renovation and reclamation, is the examination and alteration of a subject system to reconstitute it in a new form and subsequent implementation of the new form.
Reengineering generaly includes some form of reverse engineering (to archive a more abstract description) followed by some form of forward engineering or re-structuring.

Es decir reingeniería es alterar un sistema existente y cambiarlo de forma para permitir su evolución. Esta tarea incluye entender lo que ya existe mediante ingeniería reversa:

Reverse engineering. Is the process of analyzing a subject system to
identify the system’s components and their interrelationships and
create representations of the system in another form or at higher level of abstraction.

La ingerniería reversa solo abarca la parte de examinar un sistema existente. Esta se puede dividir en dos sub-areas:

Redocumentation. Redocumentation is the creation or revision of a semantically equivalent representation within the same abstraction level.

Design Recovery. is a subset of reverse engineering in which domain knowledge, external information, and deduction or fuzzy reasoning are added to the observations of the subject system to identify meaningful higher level abstractions beyond those obtained directly by examining the system itself.

En ejemplos: Redocumentation es lo que hacen herramientas como Enterprise Architect que generan diagramas UML de clase a partir del código existente. El problema con este tipo de herramientas es que están al mismo nivel de abstracción que el código actual: recorrer un diagrama de clases de este tipo no es muy diferente a utilizar un “browser” de código en un IDE.

Sin embargo para poder comprender un sistema grande se necesita poder recrear otro tipo de información. Por ejemplo poder identificar módulos (más alla de la división de paquetes en el código)  y relacionarlos con la funcionalidad o poder entender la evolución del sistema y su relación con diferentes refactorings, funcionalidad y defectos.

Design Recovery se refiere a este segundo tipo de herramientas, y es donde se concentra el research en el área de reingeniería.

Para poder llegar a hacer herramientas de Design Recovery, primero es necesario tener herramientas que puedan analizar el código. Este tipo de herramientas se llaman herramientas de Program Analysis y las hay de tres tipos: estático (analizan la estructura del código), dinámico (analizan las interacciones en runtime) e histórico (analizan como el código fue cambiando historicamente). Hay varios desafios en el area de program analysis, para hacer un resumen de ellos:

  • análisis estático: lidiar con diferentes lenguajes de programación o scripting en un mismo sistema.
  • análisis dinámico: lidiar con enormes cantidades de información, un simple “trace” de una aplicación real puede ser enorme.
  • análisis histórico: lidiar con herramientas de SCM y el hecho que los “eventos” en el tiempo no son registrados de una forma uniforme.


Hacer Design Recovery significa buscar crear un modelo mental del sistema que permita comprenderlo, lo cual implica buscar formas de visualizar el software. Esto nos lleva a las herramientas de visualización.

Crear visualizaciones de un software no es fácil, por que hay muchas “dimensiones” e interrelaciones, asi que un área de estudio importante es como crear visualizaciones que permitan entender un sistema grande más facilmente.

En el próximo post voy a hablar un poco sobre herramientas de visualización.

Por ahora que ya saben los términos usados en este área de estudio les dejo algunos links a conferencias relacionadas con reingeniería de software: