Me ha quedado un título un poco friki pero a quienes programan en Java y han tenido que tratar alguna vez con unidades de tiempo les sonará. Y es que muchas veces seguimos haciendo las cosas como siempre sin fijarnos si existen ya alternativas que nos puedan ayudar un poco a que nuestro trabajo sea más sencillo.

Desde hace tiempo está disponible en el juego de librerías estándar de Java, concretamente desde la versión 1.5, la clase TimeUnit. Esta clase representa intervalos de tiempo con distinta granularidad y provee de métodos de conversión entre diferentes unidades.
De esta forma, si queremos crear una constante con el valor de cinco minutos en milisegundos para poder utilizarla en nuestra clase, podríamos hacer:

Además del método convert, podemos usar métodos propios de cada una de las unidades de tiempo para convertirlas a otra unidad con un código más fácilmente legible:

La segunda características más destacable de TimeUnit es poder para la ejecución del hilo actual de una forma sencilla. Así, frente al uso del método sleep de la clase Thread podemos hacer:

Como vemos, TimeUnit no hace nada nuevo que no pudiera hacerse antes. Sencillamente, lo hace de un forma más expresiva que hace que nuestro código transmita mejor su intención (para qué está hecho) al hacerlo más limpio y directo.


El tema que vamos a tratar está relacionado con un tipo especial de testing automático que no suele ser frecuente en nuestro trabajo diario: las pruebas unitarias de clases abstractas.
Normalmente somos consumidores de librerías, utilidades y frameworks que nos dan el trabajo ya hecho para que podamos usar sus clases de forma sencilla y sin tener que preocuparnos de nada (o, mejor dicho, de casi nada).
En cualquier framework que analicemos, veremos que existe multitud de interfaces y clases abstractas. Nosotros nos limitamos en muchos casos a ir incorporando implementaciones y subclases concretas para que nuestras aplicaciones funcionen. En estos casos, las pruebas unitarias que automatizamos prueban estas implementaciones concretas y se enfrentan a problemas típicos como eliminar las dependencias de componentes externos (ya sea mediante mocks, stubs o algún otro tipo de test double).
Pero, ¿qué ocurre cuando somos nosotros los que estamos desarrollando un framework? ¿Cómo podemos probar de forma efectiva nuestro código si éste está plagado de componentes abstractos (ya sean interfaces o clases) y no podemos instanciar las clases más básicas que queremos probar?
Algunos pensarán que difícilmente van a tener que desarrollar un framework alguna vez. Sin embargo, esta opción no están tan alejada de nuestras posibilidades. A continuación os presento una forma sencilla de framework que podemos crear nosotros mismos: el patrón Template Method.

Template Method

Wikipedia:

es un patrón de diseño enmarcado dentro de los llamados patrones de comportamiento, que se caracteriza por la definición, dentro de una operación de una superclase, de los pasos de un algoritmo, de forma que todos o parte de estos pasos son redefinidos en las subclases herederas de la citada superclase[1].
La estructura básica de clases que le da soporte sería:


Esto nos recordar a otro patrón de diseño parecido que también habla de algoritmos: el patrón Strategy. La diferencia fundamental entre Template Method y Strategy es que, en el primero, las subclases modifican una parte del algoritmo mientras que, en el segundo, las implementaciones de la estrategia cambian el algoritmo completo.

Como podemos ver, el patrón Template Method nos da la base para construir un framework con la estructura básica de los pasos que hay que seguir para realizar una determinada acción, dejando que sean las clases concretas las que completen los detalles necesarios. Veamos ahora cómo lo vamos a aplicar en un ejemplo y de qué forma podemos hacer pruebas unitarias a la clase abstracta que define el método plantilla.

Pongamos un poco de orden

Vamos a hacer un ejemplo muy sencillo donde aplicaremos el patrón Template Method. En concreto, se trata de una clase que define el algoritmo para ordenar una lista de cadenas de caracteres.


La clase SortAlgorithm define un algoritmo genérico para la ordenación de una lista de cadenas de caracteres básandose en la implementación concreta del método compare, que se encarga de hacer la comparación básica entre dos cadenas de caracteres cualesquiera. Su implementación en Java sería algo parecido a:

    public List<String> sort(List<String> unsortedList) {
        List<String> sortedList = new ArrayList<String>();
        Iterator<String> it = unsortedList.iterator();
        while(it.hasNext()){
            String element = it.next();
            boolean inserted = false;
            for(int i=0;!inserted && i<sortedList.size();i++){
                if(compare(sortedList.get(i), element)>0){
                    sortedList.add(i, element);
                    inserted = true;
                }
            }
            if(!inserted){
                sortedList.add(element);
            }
        }
        return sortedList;
    }

    protected abstract int compare(String a, String b);

Por supuesto, no vamos a dejar nuestro método de ordenación de listas sin probar (eso no lo hacemos nunca, ¿verdad?). Así que, ¡vamos a crear una buena prueba unitaria!

La opción de testing más sencilla que podría llegar a funcionar

Como no podemos instanciar la clase SortAlgorithm por ser abstracta, lo primero que podríamos hacer para poder probarla unitariamente es proporcionar una subclase de prueba que nos dé una implementación por defecto para el método compare. De esta forma, podríamos hacer:

public class SortAlgormithmConcreteTestSubclass extends SortAlgorithm {

    @Override
    protected int compare(String a, String b) {
        return -1;
    }

}

Con esta implementación del método compare, la cadena a siempre se considerará menor que la cadena b por lo que, con nuestro algoritmo de ordenación genérico, los elementos se irán incorporando a la lista ya ordenada siempre al final. Así tendríamos que el algoritmo de ordenación nos devuelve siempre una lista igual que la primera. El test unitario para el método sort sería el siguiente:

public void testAlgorithm() {
    List<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");
    aList.add("c");
    aList.add("d");
    aList.add("e");
    SortAlgorithm sortAlg = new SortAlgormithmConcreteTestSubclass();
    List<String> anotherList = sortAlg.sort(aList);
    assertEquals(aList, anotherList);
}

Se trata de una buena implementación de una subclase específica de prueba ya que:

  • La implementación de compare es trivial (una línea de código) y nos devuelve un resultado predecible (útil para la prueba que vamos a programar).
  • Hace que probar el método sort sea sencillo pues sólo tenemos que comprobar que lo que nos devuelve es una lista igual que la original.
  • No sirve realmente para ordenar listas de cadenas de caracteres. Es decir, no estamos dando una implementación concreta del método plantilla que sirva para nada. Su propósito es única y exclusivamente permitirnos hacer pruebas unitarias de nuestro algoritmo con lo que no estaremos nunca tentados de utilizar esta implementación en nuestro código de producción.
Sin embargo también tiene algunos inconvenientes:

  • El test es más difícil de entender ya que, quien lo lee, tiene que buscar en la implementación específica de prueba, SortAlgormithmConcreteTestSubclass, para saber qué hace.
  • Hemos tenido que crear una nueva clase únicamente para poder hacer un test. En nuestro caso, ha sido una clase sencilla pero en otros podría ser mucho más complicada. Como, al final, todo el código que tenemos no es más que inventario, debemos intentar reducirlo al mínimo posible. Esto es más verdad aún para el código de test.

Haciéndolo fácil con Mockito

Este proceso lo podemos simplificar mucho utilizando alguna librería que nos ayude en la tarea de crear un comportamiento predeterminado para el método abstracto compare, de forma que así podamos probar convenientemente nuestro algoritmo de ordenación genérico. Y aquí es donde entra en juego Mockito.

En este caso, vamos a utilizar Mockito para dar comportamiento únicamente al método abstracto compare, mientras que seguimos utilizando toda la lógica ya implementada en el método de ordenación, sort. Nuestro test unitario quedaría ahora así:

    public void testAlgorithm() {
        List<String> aList = new ArrayList<String>();
        aList.add("a");
        aList.add("b");
        aList.add("c");
        aList.add("d");
        aList.add("e");
        SortAlgorithm sortAlg = mock(SortAlgorithm.class, CALLS_REAL_METHODS);
        doReturn(-1).when(sortAlg).compare(anyString(), anyString());
        List<String> anotherList = sortAlg.sort(aList);
        System.out.println(anotherList);
        assertEquals(aList, anotherList);
    }

El detalle fundamental en el que tenemos que prestar atención es el haberle pasado al método mock un parámetro extra: CALLS_REAL_METHODS. Gracias a este parámetro estamos construyendo una implementación de la clase abstracta SortAlgorithm que utiliza los métodos ya definidos en ésta y únicamente proporciona un comportamiento específico cuando se hacen llamadas al método compare con cualquier par de cadenas de caracteres, devolviendo siempre -1 (lo que coincide con la implementación que ya habíamos hecho en SortAlgorithmConcreteTestSubclass, que ya no es necesaria y puede ser deshechada).

De esta forma, podemos hacer pruebas unitarias a nuestras clases abstractas aunque las implementaciones que existan estén en proyectos distintos o, sencillamente, no dispongamos de ellas, y lo podemos hacer con una implementación limpia que nos genera el mínimo código necesario para llevarlas a cabo gracias a Mockito.

Código fuente


Puedes descargar el código fuente completo comprimido en ZIP aquí. O, si lo prefieres, lo puedes descargar del repositorio en Github.

Nota: Al proyecto final se le han incorporado un par de implementaciones que completan el uso del patrón Template Method.


A continuación comento las diez cosas que todo el que se dedique al desarrollo de software de una forma más o menos profesional debería saber (junto con una breve descripción):

  1. Análisis y diseño orientado a objetos. Para que tus sistemas consistan en grupos de objetos relacionados que interactúan entre sí en lugar de un remix de líneas de código en un estilo pseudo-procedural. Si además representan el negocio de la aplicación, mejor :). Por eso, hay que tener claro qué es el análsis y diseño orientado a objetos, principios SOLID, Domain Driven Design... Si algo de esto te suena a chino, deberías empezar por el principio. Créeme, desarrollarás más rápido, tus sistemas serán más mantenibles y podrás reutilizar muchos más componente. Fundamental.
  2. Patrones de diseño. Porque hay que conocer las soluciones que ya existen para esos problemas que siempre se repiten.
  3. Refactorización. Porque nadie es perfecto y el software que hace uno menos, es necesario conocer las técnicas que te permiten arreglar ese pequeño lío que has formado (o que te han formado).
  4. Estructuras de datos y algoritmos. Porque a veces hay que hacer algo un poco más complicado que recorrer una lista y no todo se puede resolver con un algoritmo de fuerza brutal. ¿No te suena? Entonces deberías probar a hacer "divide y vencerás", "programación dinámica", crear estructuras de grafos para aplicarles algoritmos conocidos, etc.
  5. Notación UML. Porque nadie trabaja solo y, a veces, hay que entenderse con otros (o con uno mismo). Eso sí: nunca intentes generar código a partir de un diagrama (LOL).
  6. Fundamentos de sistemas operativos. Pocas cosas hay más tristes que sentarte con un desarrollador y que te diga que no sabe abrir una conexión SSH con el servidor o que no conoce el comando para listar los ficheros de un directorio en Linux porque "él trabaja con Windows". Lamentable.
  7. Testing (automatizado). "Todo el software es culpable hasta que se demuestre lo contrario". Así que, si quieres que alguien confíe en tu software, tedrás que demostrarlo. Haz pruebas que demuestren que funciona bien. ¡Y si son automatizadas, mejor!
  8. Gestión de dependencias. Al final, o desde el principio, mejor dicho, tienes que trabajar con librerías de otros. Asegúrate de que sabes controlarlas y no son ellas las que te controlan a ti.
  9. Sistemas de control de versiones. Si no quieres perder tu trabajo y además ser capaz de colaborar con otros desarrolladores, tienes que conocer las herramientas que te premiten compartir software. Si encima utilizas Git o Mercurial, podrás llamarte Desarrollador, así, con mayúsculas.
  10. Patrones arquitectónicos. Para organizar el software a gran escala, hay que tener una visión global del código y por dónde quieres que crezca. ¡Dale forma a tu código!
¿Qué añadirías tú a la lista? Se aceptan sugerencias.


¿Qué hay de nuevo, viejo?

Nuevas características de Java SE 7

Después de meses de espera, la nueva versión de Java está aquí. Junto a mejoras de rendimiento en el entorno de ejecución, Java ofrece nuevas funcionalidades en el lenguaje que presentamos a continuación.

Uso de cadenas en 'switch'

Hasta ahora, la sentencia de control switch funcionaba con valores enteros, lo que nos llevaba a tener un código similar a:

public void seleccion(int opcion) {

switch(opcion) {
   case 1:
      System.out.println("Primera opción");
      break;
   case 2:
      System.out.println("Segunda opción");
      break;
   default:
      System.out.println("Opción por defecto");
}

Lo que puede mejorarse un poco con objeto de hacer el código más expresivo utilizando constantes del siguiente modo:

//en algún lugar de nuestra clase o en alguna otra clase o interfaz
static final int PRINCIPAL = 1;
static final int SECUNDARIA = 2;

...


switch(opcion) {
   case PRINCIPIAL:
      System.out.println("Primera opción");
      break;
   case SECUNDARIA:
      System.out.println("Segunda opción");
      break;
   default:
      System.out.println("Opción por defecto");
}

La sentencia switch no sólo funciona con enteros, también puede utilizarse con los tipos de datos char, byte y short así como para las clases wrapper equivalentes (Char, Integer...). Con Java 5 también se podían utilizar enumerados, que fueron introducidos justo en esa versión.

Ahora, con Java 7, se pueden utilizar cadenas en las sentencias switch. Esto resulta interesante, ya que en muchas sentencias de control de ejecución estamos acostumbrados a utilizar cadenas, como se ve siguiendo con el ejemplo anterior:

public void seleccion(String opcion) {


switch(opcion) {
   case "Principal":
      System.out.println("Primera opción");
      break;
   case "Secundaria":
      System.out.println("Segunda opción");
      break;
   default:
      System.out.println("Opción por defecto");
}


Hay que tener en cuenta que, si el valor que se está evaluando es nulo, se elevará una excepción del tipo NullPointerException y que las opciones tienen que ser literales de cadenas o bien alguna expresión constante de tipo java.lang.String. Además, hay que saber que la inclusión de cadenas de caracteres en sentencias switch no supone ningún cambien a nivel de bytecode