Feeds:
Entradas
Comentarios

Archivos de la categoría ‘Lenguajes Programacion’


Este post no es para tratar de convenceros de que os unais a las legiones de Javascript, sino para explicar mis razones para tomar ese camino de perdición. Antes que en javascript programé en tres o cuatro lenguajes, principalmente C y Java. Por supuesto en la universidad aprendí LISP, PROLOG, ADA, varios dialectos de ensamblador y alguna cosa más, pero como nos lo he vuelto a tocar desde entonces no los considero parte de mi “toolkit”.

Yo empecé en esto de Javascript como la mayoría de vosotros, haciendo unos scripts güarrotes para validaciones y efectillos, en los tiempos del año 2000. Era un mal necesario con el que tenía que convivir al desarrollar mis preciosas aplicaciones thin-client en Java. Sin embargo sobre 2005, empecé a querer usar servicios REST sin sesión por temas de rendimiento, lo que me hizo repensar la arquitectura de mis sistemas.  Fue entonces cuando descubrí AJAX, y me quede fascinado por las posibilidades. Desde entonces fue un camino sin retorno; cada vez que aprendía más, descubría nuevas tecnologías, y reflexionaba más sobre como diseñar mejor mis aplicaciones, más enganchado me quedaba con Javascript, y desde entonces hasta ahora.

Así que, ¿por qué Javascript?

Es ubíquo

Que yo sepa, es el lenguaje que en la actualidad está más extendido del mundo. Virtualmente en cada ordenador de cada casa se puede ejecutar una versión u otra de Javascript. ¿Qué ordenador no tiene un browser? ¿Al menos un IE6? Sí, algunas personas por motivos de accesibilidad o seguridad no lo tienen disponible, pero son una exigua minoría.

¿Y en servidor? Pues ya sabeis que tenéis el node.js, tenéis Rhino, etc. En concreto en node.js se está invirtiendo cantidades ingentes de dinero. Ya está soportado en algunas plataformas cloud, como Heroku/Cedar y Windows Azure.

¿Y en desktop? Pues desde hace tiempo existe la plataforma Mozilla, ¿en que creéis que se programa la mayor parte de Firefox y Thunderbird? En una mezcla de Javascript (y C para el bajo nivel). Además Windows 8 plantea usar a JavaScript como uno de los lenguajes de preferencia para hacer aplicaciones de escritorio (otorgándole el mismo rango que a C#).

El mismo lenguaje para cliente y servidor

A día de hoy Javascript es el único lenguaje en el que puedes desarrollar por completo una aplicación web de punta a punta, desde la UI hasta el acceso a BBDD. ¿Qué tiene esto de interesante?

  • No necesitas dominar más de un lenguaje para hacer la aplicación web. Esto te permite no estar cambiando de contexto entre un lenguaje y otro, o simplemente no necesitar dos equipos de desarrollo especializado (uno de front-end y otro de server-side). Además de aumentar tu productividad, puede facilitar tener un equipo de proyecto más cohesionado.
  • No necesitas recodificar la misma lógica tanto en Javascript como en el lenguaje que estés usando en tu server-side. ¿Modelos de negocio? ¿Validaciones? Todo eso es único y debería ser reutilizable tanto en tu front-end como en tu server-side. Si lo haces todo en Javascript, terminas teniendo un montón de código común en ambos lados, que antes tendrías que haber duplicado y mantenido en dos lenguajes diferentes. Eso sí, hay que diseñar tu arquitectura de la forma adecuada para que esto se pueda cumplir.
  • Estándares de codificación y criterios de calidad unificados para todas las capas de tu sistema.
¿No veis todo esto como una “killer feature” de Javascript? Yo personalmente el día que descubrí node.js, vi el cielo abierto, y me dije “esto es la caña”.

Tipado dinámico

Confieso que este punto es muy subjetivo. Ya sabeis que soy un fanático del TDD. Y si haces TDD como es debido, al final el compilador deja de ser tan valioso. Los errores de sintaxis o de tipos son muy sencillos de detectar y arreglar usando TDD, no necesitas un compilador para ello. En los lenguajes de tipado estático, el compilador detecta este tipo de errores, pero a cambio tienes que esperar a que éste se termine de ejecutar. Además normalmente tendrás que escribir más código, para añadir toda la información de tipado que el compilador necesita. Esto hace que el ciclo de TDD sea algo más lento que en un lenguaje de tipado dinámico. Ya que TDD me va a detectar esos errores de todas formas, no me resulta rentable el “trade-off” propuesto por el compilador.

Dicho esto, si no hacéis TDD, mucho cuidado con Javascript, puede ser un buen dolor de muelas de depurar, e incluso pequeños fallos de sintaxis te pueden llevar por el camino de la amargura. Para mi lo ideal del TDD es no tener que depurar, o si lo tengo que hacer, que sea sobre 4 o 5 líneas de código bien identificadas, y que están siendo ejercitadas por un test predecible. Desde este punto de vista Javascript no plantea ningún problema.

Paradigma mixto

Es un lenguaje que te permite hacer OO hasta de dos formas diferentes, con “extends” y sin “extends”. Pero es que además también puedes hacer programación funcional con él. Esto te permite elegir el paradigma de programación que mejor se adapte al problema a resolver.

¿Tratamiento de colecciones? ¿Cálculos puros (validaciones, reglas de negocio)? ¿Programación asíncrona? Mejor el paradigma funcional

¿Modelado de negocio? ¿DDD? Mejor OO

Y por supuesto en una aplicación de verdad vas a tener que usar ambos paradigmas, y mezclarlos armoniosamente. Incluso a veces los puedes mezclar de tal forma que llegan a ser difícil de distinguir uno de otro.

Flexible

Es un lenguaje ideal para hacer DSLs internos, gracias a su gran flexibilidad. Puedes añadir y eliminar funciones al vuelo tanto en una instancia como un constructor. Esto no sólo te permite crear DSLs sino añadir nuevas capacidades al lenguaje, y realizar metaprogramación (aunque en Ruby y Groovy se puede llegar incluso más lejos).

Reconozco que esto puede ser una fuente de problemas, y es mejor evitar este tipo de funcionalidades. Por ello las últimas versiones de Javascript te permiten cerrar total o parcialmente un objeto, para evitar que se puede modificar en tiempo de ejecución. Sin embargo en algunos casos está justificado hacer este tipo de cosas. Aparte de los DSLs y quizás algunos ejemplos de metaprogración, el caso más importante es el de retrocompatibilidad. ¿Qué ocurre si estoy ejecutándome en una máquina virtual de Javascript que no soporta la API de colecciones de la última versión de Javascript? ¿Y si estoy en un navegador que no soporta alguna API de HTML5? ¡No hay problema! Con Javascript puedo detectar tal eventualidad y añadir la API que me falta de forma dinámica. Este tipo de capacidad ha permitido a Javascript sobrevivir en el infierno de incompatibilidades de los distintos browser por más de 10 años.

¿Y los WTF?

Sí, Javascript tiene algunos WTF. Sólo es cuestión de comprenderlos y saber que están ahí. Por ejemplo, los ámbitos de visibilidad no se señalan con ‘{‘ y ‘}’. En Javascript las llaves son prácticamente cosméticas y sólo sirven para agrupar el cuerpo de bucles y condicionales. Otra tema es el hoisting de variables, o el hecho de que en Javascript no existan los números enteros sino que sean todos en coma flotante. Personalmente el WTF que más me molesta es que Javascript tiende a ser “listillo”. Esto significa que intenta no fallar nunca con excepción. De ahí el horroroso e infernal sistema de coerción de tipos o el abuso de null y NaN en mucha de las librerías del lenguaje. En esto sí es verdad que se sufre.

Sin embargo los WTF de Javascript no son tantos ni tan difíciles de aprender como la gente piensa. La mala fama de Javascript viene por causas totalmente ajenas al lenguaje, los WTF de los navegadores. Tradicionalmente la implementación de los navegadores de los estándares de manipulación de HTML y de los servicios estandarizados por W3C ha sido defectuosa. Eso ha causado multitud de quebraderos de cabeza a los desarrolladores. Realmente, en la mayor parte de los casos, cuando un desarrollador piensa en las heridas recibidas en acto de servicio por parte de Javascript, normalmente debería achacarle la culpa al navegador. Desgraciadamente hasta hace poco navegador y lenguaje han estado mezclados en las mentes de los desarrolladores.

El pero

No todo es bonito en el lenguaje. Hay varios problemas con Javascript. El primero, y más importante, son los WTF arriba mencionados. El segundo es la sintaxis tan fea que tiene. La verdad de que con esto de querer simular ser C o Java la pifiaron bien. Espero que en siguientes versiones del lenguaje arreglen todo esto. Si no de momento podemos usar “strict mode”; para desactivar la mayor parte de los WTF, o usar CoffeeScript. El caso de CoffeeScript es curioso, si sabes Javascript entonces CoffeeScript te parece un simple dialecto de Javascript. Si no conoces Javascript, te parecerá un lenguaje totalmente diferente. El caso es que CoffeeScript soluciona la mayor parte de los WTF de Javascript, y tiene una sintaxis mucho más elegante. Lo tengo en el punto de mira a ver como se desarrolla.

Estos son los problemas que reconozco con el lenguaje. Después hay problemas humanos. Ese programador de Java que intenta programar en Javascript como si fuera un hermano retardado de Java (me incluyo en los culpables). Claro, intentas que una manzana se parezca a un plátano, y después vas echando pestes de lo feas que son las manzanas. No critiquemos el lenguaje sólo porque tengamos un conocimiento superficial de este, y al intentar programar como lo hacemos en nuestro lenguaje server-side, las cosas no nos salgan bien. ¡Es como si yo ahora digo que Clojure es una mierda porque no me termino de acostumbrar a su sintaxis y no puedo hacer una asignación de variable de forma sencilla! (no sería mal post :-D )

También hay que reconocer que no es un lenguaje sencillo de aprender. Primero tienes que aprender los WTF, para desarrollar instintivamente buenas prácticas que los eviten. Después tienes que ser capaz de dominar dos paradigmas de programación distintos, OO y funcional. Sólo así conseguirás sacarle toda la potencia a Javascript.

Conclusión

Desde mi punto de vista a día de hoy Javascript es el lenguaje más adecuado para desarrollar aplicaciones web. El problema es que no tiene competencia, ¿qué otro lenguaje tiene unas características similares a las anteriormente mencionadas? Yo no conozco ninguno. Ciertamente Ruby es bastante elegante, y me gusta bastante, ¡ pero sólo se ejecuta en servidor ! Java no está envejeciendo nada bien, y aunque C# es más avanzado está mucho menos extendido y tampoco se ejecute en servidor. Lo mismo puede decirse de Clojure, Scala o Groovy. Sí, podemos hacer como GWT o ClojureScript y compilar estos lenguajes en Javascript, pero, ¿con qué fin? La experiencia GWT ha resultado fallida.

En cualquier caso, a día de hoy, programes en el lenguaje que programes, si quieres hacer aplicaciones web modernas, te toparás con Javascript. Así que es mejor aprenderlo y dominarlo.

Tal vez en el futuro salga un nuevo lenguaje, con más ventajas que Javascript para hacer aplicaciones web. En ese momento tiraré Javascript a la basura y me pondré con el nuevo lenguaje, pero me temo que aun quedan 10 años para eso.

¡Espero que os hayan sentado bien las torrijas de Semana Santa!

Read Full Post »


Hola a todos, este post me lo venía pidiendo el cuerpo desde hace tiempo. Es común oírme decir que la herencia de implementación (“extends” para los javeros) es una mierda, y cada vez que lo digo veo caras de estupefacción e incluso recibo alguna que otra crítica. La verdad es que no lo entiendo bien ya que esto se sabe desde hace bastante tiempo, es más hasta tiene un nombre: “problema de la clase base frágil”, aunque yo prefiero decir que “extends es una mierda”. Incluso, algunos dicen que James Goslin, el padre de JAVA, reconoció que incluir “extends” en el lenguaje fue un gran error. Yo considero que “extends” no tiene nada que ver con la OO (al menos no con el concepto original), sino que se incluyó porque para algo tenían que servir las clases, y se pensó que se podrían usar para reutilizar código mediante la herencia de implementación.

Vamos al lío, os voy a contar una historia. Suponed la siguiente interfaz:

public interface Lista {
	void insertar(String elemento);
	void insertarVarios(Collection elementos);
	String describirContenido();
}

Se trata de una interfaz sencilla que representa una lista, con dos métodos para añadir contenido. Veamos ahora una implementación de tal interfaz:

public class ListaSencilla implements Lista {
	private List datos = new ArrayList();

	public ListaSencilla() {
		super();
	}

	@Override
	public void insertar(String elemento) {
		datos.add(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		for (String elemento : elementos)
			this.insertar(elemento);
	}

	@Override
	public String describirContenido() {
		StringBuilder descripcion = new StringBuilder("Contenidos: ");
		for (String elemento : datos) {
			descripcion.append("'");
			descripcion.append(elemento);
			descripcion.append("' ");
		}
		return descripcion.toString();
	}
}

¿Sencillo verdad? Es una implementación muy directa y no tiene mayor misterio. Ahora supongamos que a otro miembro del equipo, que no escribió la clase “ListaSencilla”, le encargan desarrollar una implementación de “Lista” que cada vez que se añada un elemento a ésta, informe a un objeto auditor de tal suceso. Como nuestro desarrollador está enfocado en el objetivo de esta nueva clase, que es informar al auditor, y no le interesa volver a reimplementar y duplicar la lógica de inserción de datos, decide reutilizar código. ¿Qué mejor manera de utilizar el venerable “extends”? Al fin y al cabo, la herencia de implementación es la verdadera ventaja de la OO y su mecanismo más potente para reutilizar código. Mirando la especificación de “Lista” y conociendo que ya hay una implementación “ListaSencilla” disponible, le sale esto:

public class ListaAuditable extends ListaSencilla {
	private Auditor auditor;

	public ListaAuditable(Auditor auditor) {
		super();
		this.auditor = auditor;
	}

	@Override
	public void insertar(String elemento) {
		super.insertar(elemento);
		this.auditor.elementoInsertado(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		super.insertarVarios(elementos);
		for (String elemento : elementos)
			this.auditor.elementoInsertado(elemento);
	}
}

El desarrollador ni se molesta en probarlo, obviamente va a funcionar, él ha respetado el contrato de la interfaz y está usando a los amiguetes “extends” y “super”. Más tarde le llega un bug, ¡resulta que el auditor es notificado por cada elemento por duplicado! Desconcertado nuestro programador recurre a esa poderosa arma de la ingeniería del software, el depurador, y consigue aclarar el misterio. La implementación del método “insertarVarios” en la superclase llama internamente a “insertar”, que como está sobreescrito en la clase hija también notifica al auditor, pero claro, “insertarVarios” de la clase hija también notifica al auditor por su cuenta, y de ahí la doble notificación al auditor. Mas claro agua, y si no lo habéis entendido, ¡depurad!

El desarrollador aprende la valiosa lección de que no sólo debe saber el contrato del código que quiere reutilizar, sino que debe analizar con detalle como está diseñada internamente la clase a reutilizar. Ya sabía él que todo esto del diseño por contrato y las interfaces no eran más que paparruchas para vender libros y cursos. Tras pensar detenidamente cambia el código y le queda:

public class ListaAuditable extends ListaSencilla {
	private Auditor auditor;

	public ListaAuditable(Auditor auditor) {
		super();
		this.auditor = auditor;
	}

	@Override
	public void insertar(String elemento) {
		super.insertar(elemento);
		this.auditor.elementoInsertado(elemento);
	}
}

Genial, bug resuelto de la mejor manera posible: borrando código. Más contento que unas castañuelas, el desarrollador se va a casa con la satisfacción del buen trabajo cumplido.

Pasa el tiempo, y un desarrollador senior, revisando la clase “ListaSencilla”, e ignorante de que esta clase tiene más descendientes que Gengis Khan, decide optimizar el método “insertarVarios”. Decide ahorrarse el bucle, y que leches, reutilizar código de la clase “ArrayList” que para eso viene en la JDK, estos desarrolladores junior sufren del síndrome NIH… La nueva versión:

public class ListaSencilla implements Lista {
	private List datos = new ArrayList();

	public ListaSencilla() {
		super();
	}

	@Override
	public void insertar(String elemento) {
		datos.add(elemento);
	}

	@Override
	public void insertarVarios(Collection elementos) {
		datos.addAll(elementos);
	}

	@Override
	public String describirContenido() {
		StringBuilder descripcion = new StringBuilder("Contenidos: ");
		for (String elemento : datos) {
			descripcion.append("'");
			descripcion.append(elemento);
			descripcion.append("' ");
		}
		return descripcion.toString();
	}
}

¡ Qué sencillez ! ¡ Qué elegancia ! No por nada es un “lead developer” que se conoce la JDK al dedillo. Otro que se va para casa inconsciente de la que acaba de armar. ¿Por qué debería preocuparse? Al fin y al cabo el contrato de la interfaz “Lista” se cumple, y para demostrarlo tiene un test automatizado de hermoso color verde. Mientras tanto, la “ListaAuditable” y posiblemente muchas más subclases de “ListaSencilla” comienzan a fallar sin previo aviso.

Al día siguiente, el “lead developer” decide arreglar el problema, mira a ver el código de “ListaAuditable” y lo solventa en menos que canta un gallo. El código queda así:

public class ListaAuditable extends ListaSencilla {
    private Auditor auditor;

    public ListaAuditable(Auditor auditor) {
        super();
        this.auditor = auditor;
    }

    @Override
    public void insertar(String elemento) {
        super.insertar(elemento);
        this.auditor.elementoInsertado(elemento);
    }

    @Override
    public void insertarVarios(Collection elementos) {
        super.insertarVarios(elementos);
        for (String elemento : elementos)
            this.auditor.elementoInsertado(elemento);
    }
}

¿Os resulta familiar ;-) ?

“Tonto desarrollador junior”, piensa el lead developer, “se nota que en vez de fijarse en la interfaz y los tests, se ha mirado la implementación de la superclase, y por ahorrarse un método la que liado…”. En fin, muy triste :-(

Algunos de vosotros pensará que he hecho trampas, que no he usado “bien” la herencia de implementación y que soy un petardo de programador. En el siguiente post explicaré como hacerlo correctamente. Pero ésto no es lo importante, lo que quiero mostrar es que tenemos un mecanismo de reutilización de código, la herencia de implementación, que es más peligrosa que una caja de bombas. Yo prefiero no usar un mecanismo con el que tengo que andarme con pies de plomo, aunque me haya leído y entendido el contrato del código que quiera reutilizar. La herencia de implementación no es segura porque rompe la encapsulación, y necesito saber cómo está implementada la clase padre para poder heredar de ella de forma segura. Todo esto se produce porque mezcla el estado e implementación privada de la superclase con la de la clase hija en una única instancia, en vez de mantenerlas separadas. ¿No sería mejor tener un mecanismo de reutilización de código que siempre fuera seguro? Sí, claro que existe, y lo veremos más adelante.

Read Full Post »


Hola a todos, pensaba hacer una reseña al uso sobre el SpringIO 2011 Madrid, pero las distintas sesiones a las que asistí me han dejado una idea bastante persistente en la cabeza: VMWare se está posicionando como clara competencia de Oracle en el mundo de las aplicaciones empresariales JAVA (plataforma JVM mejor dicho). Como la mayoría de vosotros sabeis VMWare compró SpringSource, así que ahora todas las decisiones estratégicas sobre el desarrollo de Spring se toman con la participación y el consentimiento de VMWare. Pensar otra cosa sería pecar de excesiva inocencia.

En la conferencia me percaté de que varios temas se repetían una y otra vez en las distintas charlas: Grails, Cloud Computing, noSQL y “Portable Service Abstractions”. Comentaré primero lo que se habló sobre cada uno de estos temas y después explicaré porqué todos ellos me hacen pensar que VMWare se postula como seria competencia de Oracle.

Sin saber mucho de Grails, me da la impresión que es un framework “orientado a la base de datos”. En los ejemplos que he visto siempre se habla del MVC, pero todos los modelos Grails que he visto no son más que JavaBeans (a.k.a. registros COBOL recauchutados), eso sí, codificados en Groovy, que es mas “cool”. Después tienes las vistas, que no son más que JSPs (perdonad, GSPs) y un controlador escrito en Groovy que en realidad es un controlador de Spring MVC (que es casi igual a un backing bean de JSF). Vamos, más modelo anémico. Ciertamente es mucho mejor que un modelo anémico que podamos hacer en JAVA, y sí, no hay que picar apenas código (se agradece), con lo cual considero a Grails la plataforma perfecta para este tipo de aplicaciones. Si estoy metiendo la pata perdonadme, pero ya sabéis que me hierve la sangre con este tema ¡ Si alguien sabe como hacer buena OO con Grails que me lo diga !

Dejando de lado esta diatriba, la charla a la que asistí de Grails fue muy interesante. Peter Ledbrook explicó de forma bastante clara como integrar Grails con el resto de la empresa. ¿Qué hacemos si queremos hacer un build con ANT? ¿Y con MAVEN? ¿Y si ya existe un modelo hecho con JPA? ¿Y si resulta que el administrador de base de datos nos obliga a una determinada nomenclatura? La verdad es que resolvió bastante bien todos estos escenarios, aunque la parte de integración con MAVEN me dio la impresión de estar un pelín verde. Quizás el mayor problema es cuando ya existe un esquema de base de datos que tenemos que respetar. Aquí es donde la solución que dio no me terminó de parecer clara, y me pareció que proponía que se hiciera un modelo JPA y se integrara éste modelo con Grails. Está claro que en SpringSource se han dado cuenta de que uno de los obstáculos principales para que Grails triunfe es poder integrarse con los elementos más usuales en un sistema empresarial.

Otro punto que se habló de Grails fue integrar GORM (motor de mapeo Objeto/Relacional de Grails) con bases de datos noSQL. Se mencionó este tema en la segunda key note e @ialcazar me comentó un poco sobre una sesión que se dio a este respecto. No termino de ver como se puede integrar un motor de mapeo objeto/relacional con una base de datos no relacional ¡ Precisamente una de las ventajas de una base de datos noSQL es que no necesitamos un mapeo Objeto/Relacional ! No termino de verle el sentido técnico, pero sí el de marketing. En cualquier caso creo que es una iniciativa muy interesante de la comunidad de Grails, esperemos que lo consigan sin cargarse la escalabilidad que nos da un noSQL. También noté preocupación por el rendimiento de Grails, y se ve que están trabajando muy duro para mejorarlo.

Otro tema que estuvo bastante presente fue el Cloud Computing. Durante la primera key note y alguna que otra charla se habló de que el objetivo a medio plazo es preparar a Spring para permitir desarrollar aplicaciones en la nube. Realmente no termino de ver que problema hay con las APIs actuales de Spring y la nube, así que todo esto me suena a simple marketing. En este sentido me hubiera gustado que hablaran de tecnologías como vFabric o GemFire, pero claro, aunque son parte de VMWare, técnicamente no forman parte de Spring, con lo que hubiera estado fuera de lugar nombrarlas. Tal vez cuando se hablaba de adaptar Spring a la nube se refirieran a conseguir mayor rendimiento, y sobre todo escalabilidad y elasticidad, cosa fundamental en este tipo de entornos.

Quizás en este sentido se entienda el esfuerzo por integrarse con sistemas de persistencia noSQL, que proporcionan mayor escalabilidad a las aplicaciones. Una de las charlas más interesantes fue la de Spring Data de Costin Landau. Realmente me gustó la propuesta de Spring de montar un API “estándar” para noSQL, sobre todo teniendo en cuenta que JEE no proporciona nada comparable. En concreto las APIs de bajo nivel y el concepto de “repositorio” me resultó interesante. También la idea de dar cobertura a bases de datos documentales y orientadas a grafos me resultó bastante atractiva. Sin embargo a otras propuestas, como incorporar un sistema de persistencia mixto que mantuviera parte del modelo en BBDD relacional y parte en noSQL no acabo de verles el sentido. Tal vez sea un compromiso de “marketing” para que a la gente no le de miedo adoptar noSQL, ya que seguirían teniendo el respaldo de bases de datos tradicionales. A mi simplemente me parece una postura originada por el miedo de la gente a noSQL. Ya sabéis que pienso que si el sistema tiene las consultas bien definidas, el enfoque noSQL es totalmente superior al tradicional, y que sólo usaría un sistema SQL si realmente el conjunto de consultas las va a decidir el usuario en runtime, como son el caso de bussiness inteligence y data mining por ejemplo.

En la línea de noSQL la charla sobre Redis de Alberto Gimeno fue curiosa. Personalmente pienso que Redis está un poco verde todavía, aunque a alguno le puede interesar el concepto de clave/valor avanzado. Al contrario que un clave/valor simple, el valor almacenado puede ser no sólo un array de bytes, sino estructuras de datos como contadores, listas, mapas o sets. Para cada tipo de datos REDIS proporciona un conjunto de operaciones específicas, que se pueden ejecutar de forma atómica, y extienden el paradigma clásico del clave/valor. No termino de ver por qué esta complejidad extra es necesaria, pero sí que es verdad que facilita a un recién llegado al noSQL la transición desde el mundo de las BBDD relacionales. En todo caso me alegró ver que la gente empieza a usar sistemas noSQL, ya sabéis de que pié cojeo.

Otro tema que fue hilo conductor de esta conferencia fue el concepto de “Portable Service Abstraction”. Este nombre es una forma “cool” de decir que si desarrollamos contra interfaces “estándar”, nuestro código será portable a otros entornos. Es una idea muy vieja, y es lo que intentó JEE. Como ejemplos de estas “Portable Service Abstraction” podemos tener Spring Data, Spring REST, Spring AMQP, Spring Social, Spring Mobile y Spring Android. En general me gusta el enfoque de Spring de hacer APIs sencillas, y de reaccionar de forma rápida y ágil a las necesidades de los desarrolladores. Sin embargo no es oro todo lo que reluce. No entiendo la necesidad de Spring REST, desde mi punto de vista JAX-RS era un estándar más que suficiente. De hecho ambos estándares son tan similares, que es difícil distinguir dos aplicaciones hechas sobre cada una de ellas. Por otro lado Spring Mobile y Spring Android me decepcionaron, ya que apenan aportan valor. El primero básicamente sólo te detecta el tipo de dispositivo móvil y te redirige a una página de presentación u otra en función de éste. Vamos, nada que no exista ya en las múltiples arquitecturas multicanal “clásicas”. Spring Android es sólo un cliente para hacer llamadas al servidor. Los que sí me gustaron bastante fueron Spring Social, Spring Data y Spring AMQP. En concreto este último, ya que me pareció que el RabbitMQ era genial.

Toda esta información que recibí en la SpringIO me hizo ver que VMWare está intentando atacar a Oracle por varios frentes:

  • Spring como substituto de JEE. Aunque Spring es compatible con JEE, en el fondo lo que pretende es que programemos contra sus APIs en vez de las de JEE, aduciendo motivos de portabilidad. Esto es gracioso, ya que JEE nos propone exactamente lo mismo: una aplicación programada contra JEE es portable al ser éste el estándar de iure para el desarrollo de aplicaciones empresariales. Así que, ¿cuál API es más estándar, JEE o Spring? Esta claro que Spring ha sabido evolucionar sus APIs de forma más ágil y práctica que JEE, con todos sus comités. Todo esto es una amenaza clara contra Oracle, que es la actual “dueña” de JEE.
  • Tomcat y Spring tc Server como substitutos de WebLogic (y de paso de WebSphere). Si vamos a programar contra Spring en vez de contra JEE no tiene sentido comprar un servidor JEE completo como WebLogic o WebSphere, nos basta un contenedor de Servlets como Tomcat. Para los que no lo sepáis WebLogic es de Oracle.
  • AMQP y RabbitMQ contra JMS. Aunque RabbitMQ se integra con JMS, en realidad es una alternativa viable a JMS, y a cosas como MQSeries.
  • Grails como substituto de JSF. Como ya sabréis el modelo de programación web JEE es JSF. De hecho Oracle ha invertido en su implementación de JSF, llamada Oracle ADF, y en un IDE especial para ADF basado en Eclipse. Oracle ADF y su IDE nos permite hacer aplicaciones orientadas a base de datos muy rápidamente (o eso me han contado). Está claro que SpringToolSuite junto con Grails es competencia directa de esta tecnología.
  • noSQL contra las bases de datos relacionales. Como sabéis Oracle es dueña no sólo de la base de datos relacional más famosa del mundo, sino que con la compra de Sun, puede hacerse con el control de MySQL. Obviamente apostar por noSQL es un golpe directo a la linea de flotación de Oracle. Curiosamente el único sistema noSQL del que se habló con detalle en la conferencia fue de REDIS, que está “esponsorizado” por VMWare.
  • Cloud Computing contra hosting tradicional. El Cloud Computing es un escenario que creo beneficia más a VMWare que a Oracle. El primero, ha hecho una apuesta fuerte en este sentido con vFabric y vmForce. Si tienes una instalación propia, puede venir un comercial de Oracle a venderte WebLogic y la base de datos de turno. En un cloud no. Si convences a una empresa para irse al cloud, es más que probable que sus bases de datos Oracle queden en desuso.

En definitiva, Spring pretende suplantar a JEE como plataforma estandarizada para el desarrollo de aplicaciones empresariales con JAVA. Bueno, tal vez yo sea un poco suspicaz, pero esto es lo que realmente leo entre lineas de la conferencia SpringIO. Por cierto fue un éxito de público, y en general el nivel de las sesiones y talleres fue bastante alto. Enhorabuena a la organización, ya que no creo que con 20 euros se pudiera haber hecho mejor. ¿A alguien le pareció caro? A mi no.

Read Full Post »


Andaba yo buscando un ejemplo de código para demostrar el concepto del TDD, refactor y diseño emergente, cuando mi señora me apuntó al coding dojo en Ruby, organizado por @madridrb el pasado día 30 de Diciembre, donde durante las cervezas post-evento se me ocurrió este post. El dojo, donde el señor @ecomba propuso hacer la kata de los números romanos, se encontraba con bastante más gente de lo normal, y es que este hombre tiene mucho tirón. La idea era ir turnándose cada 3 minutos en un ordenador e ir avanzando la kata. Como éramos bastantes, y no había mucho tiempo, al final realmente no pudimos avanzar lo suficiente, y tuvo que acabarla @ecomba de forma rápida y sin poder entretenerse mucho. Esto hizo que tuviera que dar algunos saltos de diseño. Posteriormente en las cervezas me enteré que muchos asistentes no estaban muy versados en esto del TDD y que estaban confusos. Sobre todo la mayor discusión trataba sobre por qué era importante hacer TDD y refactor en pasos muy pequeños y qué ventaja daba a la hora de diseñar el código. Así que en ese momento supe que mi siguiente post iba a ser un ejemplo con código sobre cómo conseguir un diseño emergente a base de Refactor y TDD en pasos pequeños (baby steps).

Este es uno de los posts más difíciles que he hecho ya que voy a intentar mostrar mis procesos mentales de diseño, y como me enfrento a un problema de programación (sencillo por cierto). Aviso a los asistentes del coding dojo que el código final al que llego en este post es diferente al que hizo @ecomba, lo que demuestra que no suele existir una única implementación “óptima”, y que el resultado puede variar en función de criterios personales y algo del azar. No tengo experiencia alguna en Ruby, sólo he hecho los koans y esta kata, con lo que he usado un Ruby muy básico y sencillo al no conocer bien el lenguaje y la librería de objetos. Sin embargo guiado por el TDD y el refactor creo que llego a una solución bastante compacta. Empecemos…

Lo primero de todo es familiarizarse con la funcionalidad a implementar. Ni el TDD ni el refactor son un sustituto de ponerse a pensar, sólo un guía para nuestro proceso creativo, por lo que debemos tener alguna idea del objetivo funcional que debe alcanzar nuestro código. No es necesario ser un experto en la funcionalidad, sino saber por donde van más o menos los tiros y no ir dando palos de ciego. Aquí va la funcionalidad de los números romanos, al menos lo que recordaba cuando me puse a tirar código:

  • Queremos convertir un número entero en un número romano. Nada más.
  • Los romanos no representaban de forma explícita ni el cero ni los números negativos.
  • Existen un conjunto de 13 símbolos o numerales básicos (nosotros tenemos 10). Cada uno de estos tiene un valor predefinido, pero ninguno representa el 0 o valor negativo.
  • Para representar un número, se concatenan estos numerales, y sus valores se van sumando hasta que se obtiene el valor del número.
  • Algunos casos como el 4 o el 9 son especiales y se representan de forma especial.

Así que llegué a mi casa, abrí el vim, me instalé el rspec y secuestré el “mug of vi” de mi mujer para que me sirviera de chuleta (soy penoso con el vi y quería aprender, que lo pasé muy mal en el dojo). Lo primero que se hace siempre es coger el caso más básico, y que cosa más básica que el número 1. En rspec queda:

describe "Roman number" do
	it "I is equivalent to 1" do
		1.to_roman.should == 'I'
	end
end

Evidentemente falla, con lo que hay que añadir una implementación. Pero siguiendo las reglas del TDD y refactor, debe ser la implementación más simple posible que pase el test, ya lo pondremos bonito después si existiera una razón de peso. Para los que no sepan Ruby, en éste lenguaje las clases son abiertas, por lo que el enfoque de diseño tomado es añadir un método “to_roman” a la clase “Fixnum” que representa a los enteros.  La implementación es obvia:

class Fixnum
	def to_roman
		'I'
	end
end

describe "Roman number" do
	it "I is equivalent to 1" do
		1.to_roman.should == 'I'
	end
end

Vamos a probar ahora con el número 5, que es otro de los numerales básicos romanos. La estrategia de momento es ir añadiendo numerales básicos, a ver que nos sale. El test y su correspondiente implementación:

class Fixnum
	def to_roman
		return 'V' if self == 5
		'I'
	end
end

describe "Roman number" do
	it "I is equivalent to 1" do
		1.to_roman.should == 'I'
	end

	it "V is equivalent to 5" do
		5.to_roman.should == 'V'
	end
end

La técnica es sencilla, copiar y pegar lo que funcionó en el caso del 1. Reemplazamos los valores adecuados y cubrimos cada caso con un if. Estamos en modo “pasar tests”, hasta el momento no hemos detectado duplicación y no hemos refactorizado nada. Pero esto ya empieza a oler a cuerno quemado. Volvemos a repetir, esta vez para el 10. La cosa queda:

class Fixnum
	def to_roman
		return 'X' if self == 10
		return 'V' if self == 5
		'I'
	end
end

describe "Roman number" do
	it "I is equivalent to 1" do
		1.to_roman.should == 'I'
	end

	it "V is equivalent to 5" do
		5.to_roman.should == 'V'
	end

	it "X is equivalent to 10" do
		10.to_roman.should == 'X'
	end
end

Ya se observa claramente la duplicación. Tenemos dos (o tres, según se mire) lineas con la misma estructura de código. Esto es normal ya que hemos estado haciendo copy&paste. Ahora toca reflexionar sobre la intención de nuestro código, ¿qué queremos hacer en realidad? ¿Refleja el código actual esa intención (es expresivo)? Es obvio que lo que queremos hacer es mapear números a numerales romanos, y que existe una correspondencia uno a uno. En realidad estamos haciendo una búsqueda de un literal romano por número entero a base de un montón de ifs. El uso de una simple Hash (o mapa o diccionario) nos elimina las líneas duplicadas y nos da un código más expresivo (si elegimos bien los nombres). Tras refactorizar me sale lo siguiente:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }

	def to_roman
		ARABIC_TO_ROMAN_NUMERAL[self]
	end
end
# .......

Por brevedad no me entretendré con los siguientes 10 numerales romanos, y pasaré al siguiente problema, ¿qué pasa si un número no se corresponde con un literal romano? Según el funcional hay que concatenar los numerales hasta sumar el número deseado. Como caso más sencillo de este escenario añado el test para el número 2 que debe transformarse en ‘II’. Además voy a refactorizar un poco los ejemplos de test, agrupándolos por escenario. En RSpec usaré un contexto por escenario (no se si esto es purista pero a mi me parece bien). El test ahora es:

# .........
describe "Roman number" do
	context "has basic numerals with different values" do
		it "I is equivalent to 1" do
			1.to_roman.should == 'I'
		end

		it "V is equivalent to 5" do
			5.to_roman.should == 'V'
		end

		it "X is equivalent to 10" do
			10.to_roman.should == 'X'
		end
	end

	context "concatenates numerals in descending order until they sum up the desired integer" do
		it "II is equivalent to 2" do
			2.to_roman.should == 'II'
		end
	end
end

Como veis un contexto para los numerales básicos y otro para los que no lo son. Añado la implementación más básica que se me ocurre, si el número coincide con un numeral básico lo devuelvo y acabo, si no, devuelvo ‘II’ a cascoporro:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }

	def to_roman
		return ARABIC_TO_ROMAN_NUMERAL[self] if ARABIC_TO_ROMAN_NUMERAL[self]
		'II'
	end
end
# ........

Añado un test para el número 3, que debe resultar en ‘III’ y su correspondiente implementación. Pero esta vez me lo curro un poco más:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }

	def to_roman
		return ARABIC_TO_ROMAN_NUMERAL[self] if ARABIC_TO_ROMAN_NUMERAL[self]
		'I' + (self - 1).to_roman
	end
end

describe "Roman number" do

#      ......................

	context "concatenates numerals in descending order until they sum up the desired integer" do
		it "II is equivalent to 2" do
			2.to_roman.should == 'II'
		end

		it "III is equivalent to 3" do
			3.to_roman.should == 'III'
		end
	end
end

Como veis esta vez he sido más sofisticado y en vez de meter un ‘III’ if self == 3, he leído bien el funcional y se me ha ocurrido un algoritmo. Siguiendo el espíritu de concatenar hasta sumar el número, se me ocurre que puedo tomar el numeral ‘I’ y restarle su valor al número que quiero convertir. El resultado de esta resta, qué es lo que queda para conseguir sumar el número deseado, lo convierto a su vez en un número romano y lo concateno. Aquí hemos tenido que parar y reflexionar sobre la funcionalidad para obtener una idea creativa. El TDD nos ha servido para llegar a un punto donde esta idea se nos pueda ocurrir con facilidad.

Sigamos, ¿qué pasa con otros casos? Añado tests para el 6, el 11 el 15 y el 20. Los hago pasar haciendo copy&paste del caso del 2 y el 3, reemplazando ‘I’ y 1, por ‘V’ y 5 para pasar el test del 11, y ‘X’ y 10 para pasar el test del número 9. La cosa queda así:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }

	def to_roman
		return ARABIC_TO_ROMAN_NUMERAL[self] if ARABIC_TO_ROMAN_NUMERAL[self]
		return 'X' + (self - 10).to_roman if self > 10
		return 'V' + (self - 5).to_roman if self > 5
		'I' + (self - 1).to_roman
	end
end

describe "Roman number" do

#      .................

	context "concatenates numerals in descending order until they sum up the desired integer" do
		it "II is equivalent to 2" do
			2.to_roman.should == 'II'
		end

		it "III is equivalent to 3" do
			3.to_roman.should == 'III'
		end

		it "VI is equivalent to 6" do
			6.to_roman.should == 'VI'
		end

		it "XI is equivalent to 11" do
			11.to_roman.should == 'XI'
		end

		it "XV is equivalent to 15" do
			15.to_roman.should == 'XV'
		end

		it "XX is equivalent to 20" do
			20.to_roman.should == 'XX'
		end
	end
end

Fijaros en el orden en que pongo las líneas de código, primero evalúo los numerales con valor más alto y después las de valor más bajo. Si lo hacemos en otro orden los tests fallan, al devolverme por ejemplo ‘VVI’ en vez de ‘XI’. En este caso los tests son los que nos han hecho darnos cuenta de que hay que ordenar los numerales de mayor a menor, y además de sumar el valor, debe ser la representación más corta posible. Esto no lo tenía yo nada claro por el funcional que indiqué más arriba. En este caso los tests nos aclaran la funcionalidad.

Sin embargo de nuevo tenemos duplicación y el código da repelús. La misma estructura de código repetida en tres líneas, sólo varía en el numeral romano usado en cada caso y su correspondiente valor numérico. ¿Podemos sustituir esta duplicación por una regla parametrizable? ¿Quizás un método auxiliar que recibiera como parámetros el numeral romano y el valor numérico? Esta solución eliminaría algo de duplicación, pero aun quedaría duplicada la estructura de la cascada de ifs. Es esto último lo que más me preocupa, ya que se viola el principio abierto/cerrado. Cuando quisiéramos añadir un nuevo numeral (cuando nuestro cliente recordara uno nuevo), tendríamos que “abrir” el método to_roman para añadir otra línea más. Esto es más grave que unos cuantos caracteres repetidos. Hay que pararse a pensar de nuevo, es hora de ganarse el sueldo. Lo que se me ocurrió es lo siguiente:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }

	def to_roman
		return ARABIC_TO_ROMAN_NUMERAL[self] if ARABIC_TO_ROMAN_NUMERAL[self]
		ARABIC_TO_ROMAN_NUMERAL.each do | arabic_number, roman_numeral |
			return roman_numeral + (self - arabic_number).to_roman if self > arabic_number
		end
	end
end
#  .............

La idea es recorrer la colección de numerales, de forma que encontremos el numeral romano de más valor, que sea inferior al número que buscamos, y usamos dicho numeral en la regla recursiva que descubrimos antes para calcular el resultado. Podríamos haber usado un bucle for, pero sospecho que no están bien vistos en el mundo de Ruby, así que uso el método iterador each, y le paso un bloque de código. En cuanto encuentro el numeral buscado devuelvo el valor y el bucle (perdón, la iteración) termina. De paso ya no necesito ese método auxiliar que se me ocurrió antes, ya que la regla recursiva sólo se usa una vez y es bastante compacta. Me encanta cuando los planes salen bien… WTF! ¡ Fallan los tests ! ¡ Qué c*** pasa ! ¡ Es la hora del debugger !

Lo que ocurre es que el método each itera las entradas de la hash en el orden que le sale de las gónadas. Es una sorpresa, ya que según la documentación, debería iterarlos en el orden en que los añades a la hash. Ya estoy por mandar un bug a la comunidad de Ruby cuando descubro mi fallo. Estoy usando Ruby 1.8.x y la documentación es de la 1.9.x. Algo huele a quemado, busco un poco y efectivamente: originalmente el orden de iteración de las hash era aleatorio, pero a partir de la 1.9.x es por orden de inserción. Esto de añadir cambios que rompen la API y cambiar sólo el “minor version” no es buena idea.

Total, que tengo una implementación que supuestamente funciona en Ruby 1.9 (no lo he probado), pero no en 1.8. Algo he de hacer. De momento me calmo un poco, y me dedico a arreglar otra duplicación de código que me está matando, la primera línea del método to_roman. Lo que hago es lo siguiente:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }

	def to_roman
		return '' if self == 0
		ARABIC_TO_ROMAN_NUMERAL.each do | arabic_number, roman_numeral |
			return roman_numeral + (self - arabic_number).to_roman if self >= arabic_number
		end
	end
end
# ..............

Directamente elimino la línea y cambio self>arabic_number por self>=arabic_number. La idea es que si encuentro un numeral que sea exactamente igual al valor buscado, también puedo aplicar la regla recursiva. En este caso el resto es 0, que como sabemos no se representa en números romanos. Se soluciona devolviendo cadena vacía como caso base de la recursividad cuando el número es 0, eliminando la fea duplicación que teníamos como caso base.

Ahora ya puedo centrarme en hacerlo retrocompatible con Ruby 1.8. Simplemente ordeno de forma explícita la hash en orden descendiente (de mayor a menor). Un cambio trivial:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 5 => 'V', 1 => 'I' }.sort.reverse

# ................

end

Dicho sea de paso, esta ordenación me devuelve un array, con lo que en vez de una hash termino con un array de pares clave/valor en la constante ARABIC_TO_ROMAN_NUMERAL. Debido a que itero con el método each, y a que realmente había dejado de usar la búsqueda por clave, el cambio es transparente.

Ahora toca enfrentarse a la tercera fase de la especificación funcional: los casos especiales. Los romanos no escribían ‘IIII’ para el número 4, sino ‘IV’. Lo mismo con el 9 que se representa como ‘IX’ en vez de ‘VIIII’ (esta regla es para los números romanos clásicos, la versión más primitiva no la tenía). Añado estos ejemplos y obviamente los tests fallan. Tal vez tenga que hacer un algoritmo de simplificación, de modo que si hay tres numerales seguidos iguales los sustituya por la versión simplificada. Parece difícil. Antes de complicarme la vida, y por si cuela, se me ocurre añadir ‘IX’ y ‘IV’ a los numerales básicos. Sorprendentemente funciona, me olvido de algoritmos complicados. Finalmente añado algunos tests con números complicados, para asegurarme que todo funciona bien. El código final es:

class Fixnum
	ARABIC_TO_ROMAN_NUMERAL = { 10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV', 1 => 'I' }.sort.reverse

	def to_roman
		return '' if self == 0
		ARABIC_TO_ROMAN_NUMERAL.each do | arabic_number, roman_numeral |
			return roman_numeral + (self - arabic_number).to_roman if self >= arabic_number
		end
	end
end

describe "Roman number" do
	context "has basic numerals with different values" do
		it "I is equivalent to 1" do
			1.to_roman.should == 'I'
		end

		it "IV is equivalent to 4" do
			4.to_roman.should == 'IV'
		end

		it "V is equivalent to 5" do
			5.to_roman.should == 'V'
		end

		it "IX is equivalent to 9" do
			9.to_roman.should == 'IX'
		end

		it "X is equivalent to 10" do
			10.to_roman.should == 'X'
		end
	end

	context "concatenates numerals in descending order until they sum up the desired integer" do
		it "II is equivalent to 2" do
			2.to_roman.should == 'II'
		end

		it "III is equivalent to 3" do
			3.to_roman.should == 'III'
		end

		it "VI is equivalent to 6" do
			6.to_roman.should == 'VI'
		end

		it "XI is equivalent to 11" do
			11.to_roman.should == 'XI'
		end

		it "XV is equivalent to 15" do
			15.to_roman.should == 'XV'
		end

		it "XX is equivalent to 20" do
			20.to_roman.should == 'XX'
		end
	end

	context "converts even complex examples (to gain more trust in our implementation)" do
		it "XVIII is equivalent to 18" do
			18.to_roman.should == 'XVIII'
		end

		it "XIX is equivalent to 19" do
			19.to_roman.should == 'XIX'
		end

		it "XXXVII is equivalent to 37" do
			37.to_roman.should == 'XXXVII'
		end
	end
end

Como veis el código es diferente al que propuso @ecomba. Curiosamente @ialcazar me mostró la solución de @cavalle que es bastante similar a la mía, seguramente porque ambos hemos optado por un enfoque recursivo. Eso sí, ninguna de las tres soluciones tiene métodos de más de 4 líneas de código ;-). Notad que he tomado dos decisiones de diseño importantes: me he decidido por un diseño recursivo, y los casos especiales, como ‘IV’ o ‘IX’, los trato como numerales básicos. ¿Qué ocurriría si hubiéramos decidido que los casos especiales no son numerales básicos? ¿A alguien le interesa explorar este camino?

¿Para que nos ha servido el TDD? En este caso nos ha servido para guiar el proceso de pensamiento. Añadiendo código poco a poco puedo detectar duplicación, violaciones de principios SOLID, y otros problemas rápidamente. Es en estos momentos donde TDD+Refactor en pasos pequeños, nos obliga a parar y pensar. Hasta que no tengamos una visión más profunda del problema, no podremos avanzar, y ésta forma de trabajar nos golpea en la cabeza obligándonos a reflexionar. Sin embargo no olvidemos que el problema de los números romanos es pura algorítmica. En casos más complejos como diseño OO el TDD brilla en todo su esplendor.

Si queréis, podéis entrar github y echarle un vistazo a todo el histórico de “baby steps” que fui haciendo. ¿Alguien se anima a hacerlo en otro lenguaje?

Read Full Post »


Lo cierto es que una de las novedades que se introdujeron en la JDK5 que han causado mayor impacto son las anotaciones. Ahora la mayoría de los frameworks e incluso estándares JSR hacen un uso intensivo de las anotaciones, son algo que esta de moda. Están tan de moda que a veces las podemos llegar a usar de forma incorrecta sin darnos cuenta. Al fin y al cabo se usan por todos lados, y si algo se usa mucho entonces es que esta bien, ¿verdad? Debemos descubrir cuando usarlas y cuando no, y de eso precisamente, hablo en este post.

Ciertamente las anotaciones resultan muy útiles y nos proporcionan una herramienta muy potente a los diseñadores de frameworks. La finalidad de las anotaciones es sencilla: permitir al programador definir metainformación con la que decorar métodos, clases, paquetes, campos y argumentos. Sí, sí, pero, ¿que es esto de la metainformación? ¿Que tiene todo esto que ver con mi JPA? La idea es que el programador puede querer añadir información que pertenece al dominio de la aplicación a un método o clase. Si estamos escribiendo un framework de persistencia, será muy útil definir qué clases son persistentes y que propiedad de la clase usar como clave primaria. Fijaros que en este caso, el del framework, el dominio de negocio es la persistencia. Si usáramos un framework de negocio el dominio de negocio seria otro.

Veamos mas en detalle el ejemplo del framework de persistencia (al fin y a al cabo algunos piensan que con tener la persistencia solucionada la aplicación ya esta hecha al 50%, solo le faltarían las pantallas). En dicho framework necesitamos indicar cuales son las clases que representan conceptos persistentes y que propiedad usar como identificador, ese es nuestro dominio de negocio.

¿Como lo haríamos antes del advenimiento de las anotaciones? Simplemente tendríamos un maravilloso ficherito XML (esa moda moribunda) donde haríamos un mapeo indicando que clases son persistentes y que campo usar de identificador. Sin embargo pronto se vio que este enfoque es bastante frágil y propenso a errores. ¿Que ocurre si cambiamos el nombre de la clase? ¿O del campo? ¿Ese nombre era con mayúsculas o minúsculas? ¿Cometí un error tonto al escribir el nombre? Mantener la consistencia entre las clases y campos y el fichero XML puede ser una tarea compleja si el proyecto tiene un tamaño decente. El problema principal está en que información importante respecto a las clases se mantiene en un fichero distinto del código fuente. Esto genera el problema de sincronizar y mantener la consistencia entre ambos ficheros. Por otro lado una persona que lea la clase no sabrá si es persistente o no, con el consiguiente problema de mantenimiento. No contentos con esto, muchos IDEs no poseen capacidades de refactorización que tengan en cuenta el fichero XML de nuestro framework de persistencia favorito.

Debido a estos problemas se importó la idea de las anotaciones desde la plataforma .NET a JAVA. Ahora el diseñador del framework puede eliminar ese fichero XML tan molesto y definir anotaciones para su dominio de negocio. Supongamos que define las anotaciones @Persistente e @Identificador.  Solo tenemos que anotar nuestro código. Solucionamos el problema de raíz al eliminar el fichero XML. Ademas tenemos junto al código fuente nuestra metainformación de persistencia en forma de anotaciones lo que aumenta la mantenibilidad. El framework ahora en vez de leer el XML, lee dicha información directamente desde la clase. Esta lectura y procesamiento de anotaciones se puede hacer en tiempo de compilación, de carga de clases, o mediante reflexión, dependiendo de como hayamos definido las anotaciones y de la sofisticación de nuestro framework. Las anotaciones definidas para ser procesadas en tiempo de compilación son usadas por IDEs y scripts de compilacion. Las accesibles en runtime por reflexión y en tiempo de carga son útiles usadas sobre todo por frameworks. El código de una supuesta clase persistente quedaría así:

// Declara las instancias de esta clase como persistentes
@Persistente
public  class Person implements Serializable {
// Declara esta propiedad como identificador
 @Identificador
 private Integer id;

 private String name;

 public Integer getId() {
 return id;
 }

 public void setId(Integer id) {
 this.id = id;
 }

 public String getName() {
 return name;
 }

 public void setName(String name) {
 this.name = name;
 }
 }

Hasta aquí todo muy bien, ¿donde está la pega? La pega está en confundir metainformación con información de configuración. La información de configuración es aquella que cambia en función del entorno en el que se ejecuta la aplicación, o bien, no esta bajo nuestro control y puede cambiar en cualquier momento. En función de la maquina donde se ejecute, la base de datos, o el servidor de correo que usemos, la información de configuración puede cambiar, o simplemente el administrador puede decidir en cualquier momento cambiar ese nombre de usuario tan importante. Por eso dicha información se suele depositar en ficheros de configuración, para no tener que recompilar tu código cada vez que vayas a cambiar de entorno.  Hoy en día a nadie se le ocurre poner un usuario y contraseña a fuego en el código, ¿verdad? Tal vez estoy siendo demasiado optimista…

Volvamos a nuestro ejemplo de framework de persistencia para ver como esto nos puede llevar a la perdición. Supongamos que somos unos arquitectos muy listos y decidimos mejorar nuestro framework con una nueva anotación: @Tabla Como estamos a la última sabemos que las anotaciones pueden tener campos para ser parametrizadas. Decidimos pues que @Tabla recibirá el nombre de la tabla en la que se persiste el objeto y que en @Identificador podremos definir el nombre de la columna que contiene la clave primaria. Nuestro código quedaría así:

// Declara las instancias de esta clase como persistentes
// La persistencia sera mediante la tabla PERSONAS
@Persistente
@Tabla(tableId="PERSONAS")
public  class Person implements Serializable {
 // Declara esta propiedad como identificador, mapeado a columna PK
 @Identificador(columnId="PK")
 private Integer id;
 private String name;

 public Integer getId() {
 return id;
 }

 public void setId(Integer id) {
 this.id = id;
 }

 public String getName() {
 return name;
 }

 public void setName(String name) {
 this.name = name;
 }
 }

¿Que chulo verdad? FAIL !!!  Cuando instaleis esto en producción os encontráis con la desagradable sorpresa que por razones de nomenclatura los nombres de tabla son $entorno$_$tabla$ y la columna de la clave primaria siempre es $entorno$_$tabla$_PK ¡ Dios, que hacemos ! ¿Cambiamos el código y recompilamos, generando una versión para cada entorno? No señores, damos marcha atrás en nuestro cambio tan chulo y lo tiramos a la basura. Los nombres de las tablas y columnas de identificación hay que ponerlos en un fichero de configuración que cambia por entorno. Esto nos quita el problema de compilar pero no de reempaquetar para cambiar el fichero. Si queremos tener el mismo desplegable en los tres entornos, y ahorrarnos problemas con MAVEN y la gente de sistemas, lo que hacemos es pedir la URI donde se encuentre el fichero mediante JNDI, abrir la URI y procesar el fichero. Debemos además declarar una referencia JNDI a recurso URI. Cada entorno tiene un fichero distinto configurable por la gente de base de datos y en una URI distinta. Durante el despliegue la gente de sistemas mapea la referencia JNDI al fichero correspondiente al entorno y nosotros no nos damos ni cuenta. Existen otras formas de hacer esto, como por ejemplo jugar con el classpath y las librerías compartidas, pero eso en otro post.

Bien, si alguno leyó mi anterior post, puede invocar el poder de KISS para arrearme en “to la boca”. Cierto, si en tu proyecto hay ciertos parámetros externos a la aplicación que nunca van a cambiar puedes usar KISS y ahorrarte el fichero de configuración y poner estos parámetros a fuego en tu código mediante anotaciones. Normalmente esto de que hay parámetros que no cambian no me lo encuentro muy a menudo, ya que mis proyectos se mueven en un entorno con alta tasa de cambio e incertidumbre. Si es tu caso, enhorabuena, pero asegúrate bien antes, no tenga que decirte eso de “te lo dije…”. En el ejemplo anterior puede ser que realmente el nombre de las tablas lo decida el desarrollador (mapeo top-down) o simplemente no puedan cambiar por los siglos de los siglos.

El caso del mapeo top-down es interesante. Si el nombre de las tablas se puede decidir por parte del desarrollador JAVA, y no cambia con el entorno, podríamos invocar KISS y eliminar esa anotación @Tabla y el parámetro columnId de @Identificador ¿Como? Usando configuración por convención o nomenclatura. De esta forma el nombre de las tablas se deriva del nombre de las clases. Es un mapeo automático que nos evita la configuración, ya sea en forma de anotación o de fichero.

El confundir metainformación con configuración es tan común que muchos estándares JSR y frameworks lo han cometido. El clamor por eliminar ficheros XML y usar las anotaciones era tal que no se pararon a pensar en lo que hacían, ¿o tal vez sí? Si lo pensáis bien, los JSRs y los frameworks tienen que ser populares si quieren ser usados, y lo que te hace más popular es usar lo que esté más de moda, no lo mejor. El usar anotaciones de forma masiva, al ser una tecnología chula, hizo que esos JSR y frameworks se hicieran populares. Un truco muy hábil. O al menos eso es lo que mi mente me dice a veces cuando me pongo en modo paranoico.

Así que ya lo sabéis, usad las anotaciones, diseñad frameworks que las aprovechen, pero no caigais en la tentación de usarlas de cualquier manera y para cualquier cosa.

Read Full Post »

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 41 seguidores