Implementación del patrón Singleton en Scala

Saludos a todo el mundo (o todo el mundo que lea este blog). Como estoy un poco liado con otras entradas referentes a la charla de integración continua en Agile-Canarias, hoy hablaré de un tema sencillo que me ha surgido esta semana. Como implementar el patrón Singleton en Scala.

Para empezar y para todos aquellos que no sepan lo que es el patrón Singleton, se puede resumir en que es un patrón de diseño que permite mantener una única instancia de un determinado objeto. Esto es muy útil cuando se trata de un objeto cuya instanciación es muy costosa o simplemente porque modela un elemento que es único en la realidad.


A partir del diagrama de clase anterior nos podemos hacer una idea de su implementación en un lenguaje como por ejemplo Java, pero para que quede más claro, aquí va el código de un Singleton en Java :-) Esta sacado de la Wikipedia y es tan solo un ejemplo ilustrativo así que no le intentéis buscar un sentido más allá de ser un simple ejemplo (aunque como ejemplo funciona bien).


public class Singleton {

private static final Singleton instance = new Singleton();

...

// El constructor privado impide la instanciación
private Singleton() {}

public static Singleton getInstance() {
return instance;
}

...

}

Ahora bien, si en Scala no disponemos del modificador static, cómo vamos a hacer esto?!?!?!?! Pues es muy sencillo, igual que hacemos para simular los métodos estáticos, con la declaración object en vez de class, es decir.


object SingletonContainer {

val singleton = ... ; // instanciación desde una Factoría

def getInstance: Singleton = {
return singleton;
}

}

Como imagino que ya os habréis fijado, esta clase Java que yo he llamado Singleton, en la vida real tendrá más líneas de código referentes a su utilidad en el sistema que estemos implementando, pero para nosotros estarán contenidas en los puntos suspensivos (...) En Scala no es posible hacer un new Singleton, porque se trata de un object, pero podemos tener una factoría para crear una instancia del objeto deseado "de la clase Singleton", mientras que SingletonContainer sirve para mantener la referencia única.

Además como a todos nos gustar escribir poco, voy a dar una aproximación aún más corta a la implementación con Scala.


object SingletonContainer {

val getInstance = ... ; // instanciación desde una Factoría

}

Más corto imposible :-) Pero seguro que muchos esto les suena raro porque el patrón Singleton suele ser autocontenido, es decir que no hay contenedor que instancia a otra clase mediante una factoría. Pues claro!!! Bien, por defecto en Scala un object es un Singleton en sí mismo, así que realmente no teníamos que hacer nada de nada, ¿por qué he hecho yo esto? Porque en algunos casos es simplemente necesario tener una instancia única que en realidad es de otra clase distinta, y este es el método más sencillo que he encontrado, de forma que mi código real es algo como lo siguiente.


object BaseSemanticModel {

val getInstance = SemanticUtils.loadModelFromResourceFile(...);

}

De esta forma ahora tengo una instancia de Model (que es una clase de Jena, lo cual no viene a cuento ahora :-) que funciona como un Singleton en tan solo unas pocas líneas. Pero si BaseSemanticModel fuese la clase de la que quiero el Singleton, no tengo que hacer nada más que la implementación de la misma, porque luego la referencia a BaseSemanticModel es única y funciona como un Singleton en Scala.

Espero que me hayáis entendido, porque creo que ni yo mismo lo hago y que le sirva de algo a alguien.

Trucos al aplicar TDD

La semana pasada estuve leyendo una entrada del blog "the urban canuk, eh" que trataba sobre Test-Driven Development y he de admitir que me parecio una entrada bastante interesante con una serie de consejos que considero recomendables. Me gustaría hacer un resumen de los puntos que me parecieron más interesantes.

Utilizar nombres consistentes en las clases de tests.

Es conveniente que todas la clases de tests tengan un sistema de nombres consistente y homogéneo, por ejemplo que todas ellas tengan nombres como <TargetClass>TestCase (es decir el nombre de la clase que deseamos probar seguido de TestCase), y no que una se llame LoginTest, otra RepositoryTestCase, o lo que es peor aún que incluso ni siquiera se haga referencia a que es una clase de tests. Esto logrará que cualquier persona que trabaje en el proyecto pueda localizar los tests de forma sencilla y rápida.

Usar los mismos espacios de nombre para tests y código a probar.

He visto más de una vez que hay mucha gente que coloca las clases a probar en un paquete y las clases de tests en otro paquete con nombre diferente, con esto no quiero decir que no sea posible o no se pueda hacer, pero creo que si se ha de evitar en la medida de lo posible. En mi experiencia personal he llegado a la conclusión de que la mejor práctica es dividir entre el código de tests y el código que se desea testear en dos localizaciones diferentes, por ejemplo /src para el código fuente y /test para el código de tests (por ejemplo separado en distintas carpetas de código). Pero la estructura de paquetes ha de ser la misma para facilitar la busqueda en caso necesario.


Usar siempre los mismos nombres para los métodos de setup y teardown.

Con JUnit hay una serie de métodos especiales anotados con anotaciones como @Before o @After y aunque el método puede tener un nombre seleccionado por el programador, es recomendable que los nombres seleccionados para estos métodos sean siempre los mismos para que el código sea lo más homogéneo posible.

Nombrar los métodos de test según la funcionalidad a probar.

Dado que en TDD el primer paso es crear los tests para luego crear el código fuente, lo más normal es crear los métodos de test con un nombre que indique la funcionalidad que se desea probar. En general, es común que un mismo SUT (subject under test) tenga más de un test asociado, por lo que usar el nombre de la funcionalidad a probar documenta mejor el sistema y da una idea más concreta de lo que hace.

Documentar brevemente los métodos de test más complejos.

Hay veces que el dominio en el que trabajamos es muy complejo y el test no queda absolutamente claro, en estos casos es necesario hacer un breve comentario de lo que se desea probar en el código, para que la persona que venga detras sepa lo que estamos haciendo.

Si necesitas utilizar más de un par o tres de línea para documentar un método de test o necesitas documentar absolutamente todo porque nada queda claro, planteate que a lo mejor estas haciendo algo mal y es mejor darle un par de vueltas antes de continuar.

No crees implementaciones antes de los tests.

Si creas una implementación y luego creas los tests para esa implementación, estas haciendo pruebas unitarias, pero eso no es TDD. Intenta crear los tests antes porque en caso contrario pierdes todas las ventajas que aporta usar TDD.

Para acabar me gustaría hacer algo de publicidad sobre el libro escrito por Carlos Blé y sus colaboradores, para todas aquellas personas interesadas en el tema de TDD :)

Mientras funcione ...

Hace poco escuché a un desconocido decir algo como - Lo importante es que el código funcione correctamente no si se lee mejor o peor - no dudo ni por un minuto, que es mejor un código mal hecho que funcione, a un código perfectamente legible que no funciona. Dándole un par de vueltas y teniendo en cuenta que yo no había escuchado el resto de la conversación, imagino que lo que quería decir esa persona es que si el código mal hecho pero que funciona se puede hacer en 30 minutos en vez de en 60 es mejor, pero ahí si que discrepo totalmente.

Cuando estaba en la universidad, esa máxima era cierta, lo importante era tener la práctica terminada y funcionando (total, una vez corregida nadie la volvía a mirar). Pero cuando empecé a trabajar me dí cuenta de algo muy duro - algo me había comentado algún profesor pero de manera un poco abstracta y sin mucho detalle - ¡el software hay que mantenerlo!, ¿cómo?, a mi nadie me había enseñado eso, en que clase dieron esa parte?!?!?!

Pues sí, esa es la dura realidad, el software tiene un mantenimiento, y puede ser realmente caro si no se hacen las cosas correctamente desde el principio. Ese programador que tarda 30 minutos en hacer una funcionalidad X, dentro de 3 meses puede que tarde 5 horas en descubrir cual es el error de la aplicación simplemente porque no entendía su propio código (cuando es el suyo, porque si es de otra persona puede tardar 15 horas). Pero el programador que tardó 60 minutos, porque quería tener un código legible y usar patrones para que el código fuese más limpio, puede que tan solo tarde 1 hora en resolver el problema.

Aunque estas cifras no son científicas, creo que están muy próximas a lo que puede ocurrir en el mundo real, o simplemente a mi propia experiencia :) Con lo cual tenemos, que el programador tardó el doble en programar el método (lo que hace un total de 30 minutos de perdida), pero realizó la corrección del error 5 veces más rápido (lo que hace que gane 4 horas). Es pura lógica, yo prefiero ganar esas 4 horas!!!

Nota: Para todos aquellos que no realizan el mantenimiento de su propio código (y para que quede constancia, en general yo me encuentro en ese caso actualmente), estos consejos son igual de válidos. Si el código se mantiene facilmente, el cliente estará más contento y más contratos tendremos, además le haremos la vida más feliz a nuestros compañeros si son ellos los que mantienen el código, haciendo que nuestra empresa sea un sitio mejor. Y si lo mantiene otra empresa simplemente le harás la vida más feliz a otra persona que hará dicho trabajo, lo cual te hará mejor persona o mejorará tu karma, o cualquier otra explicación que te guste :)