Generics (I) – Introducción

Java Generics es una de las características más solicitadas de Java (casi desde la primeras versiones). La implementación vio la luz en el año 2004 (offtopic: año en que desgraciadamente falleció el gran Miguel de Guzmán).

Previo

Me gustaría hacer un inciso sobre los posibles errores que pueden existir en una aplicación. Voy a tomarme la libertad de clasificarlos en tres conjuntos que, aunque totalmente inventados, creo que pueden ser considerados perfectamente válidos:

  1. Errores en tiempo de compilación. Los mejores errores que podemos tener, ya que los detectamos en el momento que compilamos la aplicación. Estos errores son los más fáciles de detectar.
  2. Errores en tiempo de ejecución. Errores que pasan desapercibidos para el compilador. Sólo aparecen mientras el sistema está en funcionamiento, ya sea durante su testeo (todo tipo de pruebas) como durante su explotación (cuando el cliente lo está usando).
    1. Errores en tiempo de ejecución chungos. Dentro de los errores en tiempo de ejecución, me inventaré la categoría «chungos», aquellos errores que por unas causas o por otras los detecta el usuario final en lugar de los desarrolladores (o el equipo de pruebas). Vienen a ser la maldad convertida en bug.

Pero … ¿por qué usar generics?

Supongamos que en algún momento a lo largo de nuestra vida hemos llegado a tener el engendro de clases que se muestra en la figura.

Jerarquía de alimentos

Vemos que tanto verdura como carne son comida y pueden ser cocinadas dando lugar a comida cocinada (menos mal). Por otro lado, vamos a suponer que tenemos una clase que se corresponde con un cocinero de comida vegetariana. Éste tomará una colección de verduras y las devolverá bien hechas para que se las coma el usuario:

class VeganChef {
  // ...
  public Collection cook(Collection vegetables) {
    List cookedFood = new ArrayList();
    for(Object obj : vegetables) {
      Vegetable vegetable = (Vegetable) obj;
      cookedFood.add(vegetable.getCooked());
    }
    return cookedFood;
  }
  // ...
}

(Me he tomado la libertad de usar un bucle de tipo for-each, porque me resultan más cómodos que iteradores a saco. De todas formas, el bucle de la forma for-each no se introdujo hasta la versión 1.5.)

Como podemos ver, el código fuente no parece tener ningún error. El problema es que, a priori el código es completamente correcto y compilará sin problemas. De hecho, normalmente no debería dar problemas en ejecución. Pero ¿qué sucedería si en lugar de…

//...
Set vegetables = new HashSet();
vegetables.add(new Bean());
vegetables.add(new Carrot());
Collection cookedFood = veganChef.cook(vegetables);
//...

… hubiésemos escrito lo siguiente?

// ...
Set integers = new HashSet();
integers.add(new Integer(1));
integers.add(new Integer(2));
// ...
Collection cookedFood = veganChef.cook(integers);
// ...

Lo que ocurriría es un gran error en el punto que hacemos el casting, ya que un entero no es una verdura (de momento):

Vegetable vegetable = (Vegetable) obj; // ERROR!

El problema de este error es que es indetectable en tiempo de compilación. En nuestro caso fallaría en el momento de hacer el casting y quizá sería fácilmente detectable en algún punto durante la fase de pruebas. El verdadero problema es, cuando seis semanas después tenemos que nuestra jerarquía de clases ha sido modificada:

Jerarquía de alimentos

y alguien «sin querer» ha escrito lo siguiente:

// ...
Set food = new HashSet();
food.add(Ternera);
food.add(Cordero);
// ...
Collection cookedFood = veganChef.cook(filets);
// ...

Esto es un problema. El error es prácticamente indetectable hasta que el usuario final (en nuestro caso, el pobre comensal vegetariano) da la voz de alarma. Y que el usuario de un producto se sienta beta-tester cuando no lo es no deja muy buena impresión. A estos errores me refería cuando al principio del post decía «chungos».

Solución

Usando generics, nuestro método de cocinar podría quedar como sigue:

class VeganChef {
// ...
public Collection cook(Collection<Vegetable> vegetables) {
List cookedFood<Vegetable> = new ArrayList<Vegetable>();
for(Vegetable vegetable : vegetables) {
cookedFood.add(vegetable.getCooked());
}
return cookedFood;
}
// ...
}

Estamos indicando que lo que recibimos es una colección de verduras, de tal forma que en los dos ejemplos anteriores nos daría un error de compilación:

Set filets = new HashSet();
filets.add(Ternera);
filets.add(Cordero);
// ...
Collection cookedFood = veganChef.cook(filets); // ERROR
// ...

Esto es una gran ventaja. Hemos bajado un error del nivel de error de ejecución chungo (nos falla en ejecución, y probablemente en un caso que no habíamos contemplado y que se ha «comido» el usuario final) en un error en el nivel de compilación (los más fáciles de arreglar). Hemos conseguido hacer la programación genérica en Java menos propensa a errores.

Para finalizar (de momento)

No obstante, el uso de Generics no está exento de sus peculiaridades. Si las usas, habrás oído decir, con mejores o peores palabras, que las Generics en Java no tienen nada que ver con las Templates de C++. Bueno, en realidad ambas son mecanismos para facilitar el concepto de programación genérica en ambos lenguajes, pero sí es cierto que la forma de comportarse de ambos es bastante diferente.

En los próximos artículos se resaltarán estos aspectos, además de cómo usar clases que se declaran como genéricas e incluso cómo declararlas nosotros como solución a determinados problemas. Espero que os haya gustado. Tanto si sí, como si no, ¡ahí tenéis los comentarios!

PD: Los diagramas han sido generados a partir de la aplicación http://yuml.me/. Es Web, gratuita (aunque existe versión de pago) y permite generar fácil y rápidamente diagramas de secuencia, de clases y de casos de uso.

Cosas básicas que [quizá] no sabías sobre Java (II)

Continuamos la serie de posts sobre cosas que quizá desconocías de Java básico. En la primera entrega hablábamos sobre atributos, variables, convenciones de nombrado y una pequeña introducción a palabras reservadas. En esta segunda entrega nos centramos en las palabras reservadas y literales, haciendo un pequeño inciso sobre Unicode.

En la pregunta que hicimos en el post anterior sobre cuáles eran palabras reservadas de entre las propuestas (if, true, for, goto, select, const), la respuesta correcta era la b) if, for, goto, const. Ahora entenderemos el porqué.

Palabras reservadas y literales

Aunque «const» y «goto» son palabras reservadas de Java, no están implementadas. Y por lo tanto, tampoco pueden usar como identificadores.

Un literal de clase (class literal) se forma poniendo el apéndice «.class» a cualquier nombre de clase existente. Evidentemente tampoco se puede usar como identificador, ya que tendría un «.» (que no está permitido para nombrar).

«true», «false» y «null» no son palabras reservadas, son literales. Y, evidentemente tampoco se pueden usar como identificadores.

Existen 50 palabras reservadas en Java (en versiones superiores de 5.0).

Unicode

Se pueden usar escapes Unicode (como ‘u00F1’ para la ñ) y, si el editor lo permite, también los caracteres especiales (como ñ) para identificadores. ¡Flipa! Podríamos declarar variables año, en lugar de la archiconocida «anio», «anyo» o incluso, la que más me divierte ver en el código: «ano».

Los caracteres Unicode se representan como la cadena ‘u’ seguida de un código hexadecimal de 4 cifras: ‘uFFFF’

Asignaciones

No puede asignar null a una variable primitiva. Da un error de compilación de tipo «Type mismatch».

Es decir, el siguiente extracto de código daría error:

    // Esto da error de compilación 
    int a = null;

Algunas cosas pueden parecer tonterías, o perogrulladas, pero si no se sientan las bases, nunca se puede llegar a dominar un lenguaje de programación. No sería el primer «experto» que falla la pregunta de test hecha más arriba.

Accesos directos

Cosas básicas que [quizá] no sabías sobre Java (I)

Este es el comienzo de una serie de posts sobre conceptos básicos del lenguaje Java. Nos centraremos en Java Standard Edition desde la versión 5.0 en adelante. No es un curso de programación, tampoco es un listado exhaustivo, es más bien un compendio de curiosidades que se nos pueden haber olvidado, o quizá nunca supimos. Comencemos.

Inicialización de atributos y variables

Siempre nos han dicho que es una buena práctica inicializar las variables antes de usarlas. De hecho, lo es, pero no siempre es necesario hacerlo. El compilador de Java inicializa los atributos de una clase, que no han sido previamente inicializados, a su valor por defecto.

Estos son los valores que pone por defecto (tipo/clase: valor por defecto):

  • byte: 0
  • short: 0
  • int: 0
  • long: 0L
  • float: 0.0f
  • double: 0.0d
  • char:‘u0000’
  • boolean: false
  • String (o cualquier objeto): null

Pero las variables locales (variables declaradas dentro de un método) nunca son inicializadas por el compilador a sus valores por defecto.

Restricciones de nombrado

Un nombre de variable/clase puede ser cualquier identificador (salvo palabras reservadas y literales) compuesto por una secuencia «ilimitada» (entendiendo ilimitada como que el lenguaje de programación no pone una restricción explícita) de letras y números Unicode, comenzando por cualquiera de las siguientes:

  • una letra
  • el símbolo del dolar («$«)
  • un guión bajo («_»)

Sin embargo las normas de convención Java no recomiendan usar $, ni _ para comienzos, y el _ usarlo exclusivamente para separar palabras en nombres de constantes.

Palabras reservadas y literales

Si te pregunto ¿cuál de estas palabras son palabras reservadas de Java?

  • if
  • true
  • for
  • goto
  • select
  • const

Sin hacer trampas ¿con cuál de las siguientes opciones te quedarías?
a) goto, select, const
b) if, for, goto, const
c) if, true, for

Probablemente, si programas en java, haciendo memoria sobre las palabras reservadas que usas, tendrás una solución en mente. No te voy a dar la respuesta directamente, sino que voy a dejar que la compruebes tú mismo. También daremos la solución en el próximo post.

Ahora que sabes la respuesta correcta, proseguimos. Existen algunas confusiones con respecto a qué es una palabra reservada y, por esto, también existe confusión sobre qué es un literal. Lo primero que hay que saber es que son excluyentes: un literal no es una palabra reservada, ni una palabra reservada es un literal.

En el próximo post hablaremos de curiosidades sobre palabras reservadas y literales, y otros conceptos interesantes.

Accesos directos

Patrones de Diseño (I – Introducción)

Uno de mis objetivos para el blog, es el de realizar un monográfico sobre los patrones de diseño: qué son, cuándo usarlos, de dónde vienen y cómo se implementan.

Así que sin más dilación, empiezo con el primero de muchos post. Espero muchas críticas y sobre todo, que os sirva de algo.

¿Qué son los patrones de diseño?

En el contexto del diseño orientado a objetos, son soluciones a problemas de diseño comunes. Para ser patrones, dichas soluciones deben poseer las siguientes características:

  1. Son reusables: para problemas de diseño similares, la solución puede ser usada como solución (de ahí la palabra «patrón»).
  2. Son efectivas: han sido usadas y probadas tantas veces que se puede confiar en la eficacia y robustez de la solución.

Evidentemente, al ser soluciones realizadas una y otra vez con la misma estructura, además del alivio virtual que nos supone que ya existe gente que ha probado el diseño hasta la saciedad, tenemos la ventaja de que parte de esa gente ha agrupado en catálogos patrones para describir en ellos los contextos en los que se pueden usar y cómo usarlos.

Dichos catálogos se pueden clasificar en dos grupos bien diferenciados, los dedicados a los patrones de diseño genéricos y los patrones de diseño específicos de un dominio (como pueden ser, por ejemplo patrones de diseño para el desarrollo de interfaces de usuario).

Para este conjunto de entradas sólo voy a hablar de los patrones definidos por el GoF en su libro “Design Patterns. Elements of Reusable Object-Oriented Software”. Éste engloba los patrones de diseño que aparecen en el «Code Complete» de Steve McConnell, además de añadir unos cuantos propios. Aún así en al bibliografía hay enlaces a otros catálogos de patrones.

En su libro, los Cuatro describían una serie de patrones que a lo largo de su carrera habían detectado como soluciones. Decidieron agrupar el catálogo de patrones en tres conjuntos que representarían su cometido: creación, composición y comportamiento.

Paso a enumerarlos y a dar una pequeña descripción. Desde luego, intentar resumir en una frase lo que significan los patrones es un crimen. Espero que los que sepan no me lapiden (mejor si me ayudan a intentar ofrecer un mejor resumen explicarlo mejor). La idea es que una vez se hayan escrito los artículos, este artículo sirva de referencia.

Patrones de creación

Son solucines a problemas relativos al proceso de creación de objetos. En el libro, los autores definen cinco patrones:

  • Abstract Factory: cómo crear objetos de la misma familia sin especificar directamente las implementaciones.
  • Builder: separa el proceso de creación de un objeto complejo de la representación del objeto en sí. Así podemos usa el mismo proceso de construcción para crear representaciones diferentes.
  • Factory Method: define la interfaz de creación de un objeto, dejando su proceso de creación a la clase que la implementa.
  • Prototype: crea objetos a partir de la clonación de objeto prototipo de la misma clase.
  • Singleton: crea un único objeto de una clase en particular.

Patrones estructurales

Éstos patrones definen cómo estructurar o disponer los objetos ante determinadas situaciones. El el libro se definen siete patrones diferentes:

  • Adapter: «adapta» la interfaz de una clase servidora a una interfaz comprensible para una clase cliente.
  • Bridge: separa de una abstracción respecto a su implementación, de tal forma que pueden ser modificadas si afectarse mutuamente.
  • Composite: cómo componer un objeto de tal forma que sus partes y el todo pertenecen a la misma estructura jerárquica (y por tanto son similares).
  • Decorator: añade funcionalidades o responsabilidades dinámicamente a un objeto.
  • Facade: especifica una única interfaz que engloba las funcionalidades ofrecidas por un conjunto de interfaces.
  • Flyweight: compartición de objetos para minimizar su uso.
  • Proxy: crea un objeto intermediario entre el objeto cliente y servidor de tal forma que podemos controlar el acceso a éste.

Patrones de comportamiento

Definen relaciones entre objetos y cómo éstos se reparten sus responsabilidades entre sí. Se definen once patrones:

  • Chain of Responsibility: una secuencia o cadena de objetos por la que pasa una petición, actuando éstos en consecuencia
  • Command: una forma de actuar ante una petición cuando no conocemos los detalles de ésta ni quién se encarga de actuar sobre ella.
  • Interpreter: definición de una gramática más una forma de interpretarla.
  • Iterator: patrón para recorrer secuencialmente una colección de elementos sin conocer realmente la implementación de dicha colección.
  • Mediator: objeto que encapsula la relación entre un conjunto de objetos evitando que los objetos se relacionen entre sí directamente.
  • Memento: almacena el estado de un objeto en un momento dado.
  • Observer: objeto/s que reaccionan en el momento que el objeto con el que están relacionados cambian de estado.
  • State: cambia el comportamiento de un objeto dependiendo del estado en el que se encuentre.
  • Strategy: define una familia de algoritmos más una interfaz que los encapsula de tal forma el el cliente use el interfaz, independientemente de la implementación de dicho algoritmo.
  • Template Method: método abstracto perteneciente a un algoritmo de una superclase que las subclases deberán implementar.
  • Visitor: o cómo separar un algoritmo de unas estructura sobre la que trabaja.

¿Cuándo, por qué y cómo usarlos?

Esto depende principalmente del contexto del problema y del patrón a usar. Estas preguntas serán resueltas en los sucesivos post dedicados a cada uno de los patrones (además de mejores explicaciones, ejemplos y dibujitos).

Saludos y disfrutad de la semana

Bibliografía

Stakeholders

Según el «Concise Oxford Spanish Dictionary», stakeholder se traduciría como «interesado». Y según Wordreference se podría traducir como «accionista», «parte interesada», «participante» o «inversor».

Existen muchos ámbitos en los que se puede definir stakeholder, pero nos centraremos en la Ingeniería del Software.

En ingeniería del software, el significado es muy similar a los anteriores (personalmente no recomiendo la traducción). Más formalmente podríamos decir que un stakholder es una persona u organización que tiene influencia – directa o indirecta – o se ve influenciado por un proceso software. También son aquellos que tienen interés en el proyecto, o gente u organizaciones que serán afectadas por el sistema o tendrán alguna influencia en los requisitos del sistema.

¿Y esto qué quiere decir exactamente? ¿Mi abuela es un stakeholder? Si usa el sistema, ha ayudado en su desarrollo, lo ha pagado o tiene algo que ver con él, sí lo es. Si está tranquilamente haciendo bolillo en casa, puedes estar tranquilo, no es un stakeholder. Con este chascarrillo, a lo que quiero referirme es que, aunque existen muchas personas que lo son, existen unos límites, todos dentro del ciclo de vida del Software al completo (desde que se idea hasta que se retira).

Los stakeholders más representativos y más fáciles de identificar pueden ser los clientes, los usuarios finales y los desarrolladores. Sin embargo existen muchos otros también relacionados con el software como pueden ser: auditores, accionistas, proveedores, directivos, administradores, etc.

Si pensamos en cualquier software que hayamos desarrollado podremos determinar primero los stakeholders obvios: el equipo de desarrollo (analistas, programadores, equipo de testing, GIP, etc.), usuarios finales (usuarios y administradores) y clientes (que son los que contratan el desarrollo del software).

Si vamos un poco más lejos, existe mucha otra gente que a priori no parecemos relacionar con este grupo, pero que giran también en torno al software. Desde la persona que firma las facturas, hasta el director de la empresa, pasando por los entrevistados en el proceso de toma de requisitos, los auditores, los colegios profesionales o los tipos que despliegan la aplicación en las «salas frías».

Una idea interesante para identificar stakeholders podría ser preguntar a cada stakeholder detectado «¿Con quién más piensa que debería hablar?» (Pressman) e ir añadiéndolos al catálogo de stakeholders.

Los diferentes stakeholders dan distintos puntos de vista sobre un mismo sistema, así que conviene al menos identificarlos y, si es posible, conocer su relaciones con la aplicación y entre ellos mismos.

Bibliografía:
Ingeniería del Software. Ian Sommerville (Google Books)
– Software engineering, a practitioner’s approach. Pressman.

Aunque pongo aquí abajo un link al libro en español, me produce urticaria visual ver «requerimientos» en lugar de «requisitos», entre otras lindezas de las traducciones técnicas…

¡Hola mundo!

Bueno, pues este es el arranque del blog. Davyria y yo llevábamos tiempo pensando en arrancar un blog para escribir un poco sobre nuestras experiencias en el mundo del software.

Con el blog nos hemos planteado un reto de intentar hacernos mejores en nuestra profesión, intentando explicar los pocos conocimientos que tenemos para quien le pueda servir de ayuda. Y por qué no, afianzar nuestras experiencias y poder expandir lo que sabemos.

¡Y quién sabe, lo mismo servimos de ayuda a alguien! Esperamos que sea así. ¡Bienvenidos al blog de nekotek!

package es.nekotek.hello;
 
class Hello {
  public static void main(String[] args) {
    System.out.println("hola mundo!");
  }
}