Mi granito de java: mayo 2011

martes, 31 de mayo de 2011

Abstract Factory

Este patrón crea diferentes familias de objetos. Su objetivo principal es soportar múltiples estándares que vienen definidos por las diferentes jerarquías de herencia de objetos. Es similar al Factory Method, sólo que esta orientado a combinar productos.

Se debe utilizar este patrón cuando:
  • Un sistema se debe configurar con una de entre varias familias de productos.
  • Una familia de productos relacionados están hechos para utilizarse juntos.

Diagrama UML



AbstractFactory: declara una interfaz para la creación de objetos de productos abstractos.
ConcreteFactory: implementa las operaciones para la creación de objetos de productos concretos.
AbstractProduct: declara una interfaz para los objetos de un tipo de productos.
ConcreteProduct: define un objeto de producto que la correspondiente factoría concreta se encargaría de crear, a la vez que implementa la interfaz de producto abstracto.
Client: utiliza solamente las interfaces declaradas en la factoría y en los productos abstractos.
Una única instancia de cada FactoryConcreto es creada en tiempo de ejecución. AbstractFactory delega la creación de productos a sus subclases FactoryConcreto.

Ahora que explique que rol ocupa cada uno en el diagrama, les pido un poco de atención en lo siguiente: veamos que relación tienen  los FactoryConcretos con respectos a los productos. Esto es, FactoryConcreto1 crea una relación entre un producto de la familia A y un producto de la familia B. Y, por otro lado, tenemos que el FactoryConcreto2 crea una relación entre otros dos productos de ambas familias.
Esto ya debería darnos una pista sobre el funcionamiento del AbstractFactory: se crea una clase por cada relación que necesitemos crear. Esto quedará más claro en el ejemplo a continuación.

Ejemplo

Hagamos de cuenta que tenemos dos familias de objetos:
1) La clase TV, que tiene dos hijas: Plasma y LCD.
2) La clase Color, que tiene dos hijas: Amarillo y Azul (los mejores colores, sin duda! ).

Más alla de todos los atributos/métodos que puedan tener la clase Color y TV, lo importante aquí es destacar que Color define un método abstracto:
  
public abstract void colorea(TV tv);

Este método es la relación que une las dos familias de productos.Dado que es un método abstracto, Azul debe redefinirlo:

public void colorea(TV tv) {
System.out.println("Pintando de azul el "+ tv.getDescripcion());
}


Lo mismo ocurre con Amarillo:

public void colorea(TV tv) {
System.out.println("Pintando de amarillo el "+ tv.getDescripcion());
}


Bien, veamos las clases correspondientes antes de continuar con nuestros ejemplo.





No es necesario implementar Clonable, no se lo necesita para este patrón.




Escenario: nuestra empresa se dedica a darle un formato estético específico a los televisores LCD y Plasma. Se ha decidido que todos los LCD que saldrán al mercado serán azules y los plasma serán amarillos. Ahora bien, una solución simple sería en la clase Azul colocar el LCD y en la clase Amarillo colocar el Plasma y todo funcionaría de maravillas. ¿Cual sería el problema? Que esta todo hardcodeado. Esto quiere decir que el hecho de que los LCD sean azules y los plasmas amarillos es una decisión del negocio y, como tal, puede variar (y de hecho el negocio varía constantemente).
Por ejemplo, que pasa si mañana Se agrega otro color o me cambian el color del LCD o mucho peor, ¿que pasa si se crea otro producto LED y también se lo quiere pintar de Azul?
Para evitar un dolor de cabeza conviene separar estas familias y utilizar el Abstract Factory:

Y los FactoryConcretos, que relacionan las familias:

Para ordenar un poco las cosas voy a crear un gestor de factorias:

Y, por último el main:


Consecuencias
  • Se oculta a los clientes las clases de implementación: los clientes manipulan los objetos a través de las interfaces o clases abstractas.
  • Facilita el intercambio de familias de productos: al crear una familia completa de objetos con una factoría abstracta, es fácil cambiar toda la familia de una vez simplemente cambiando la factoría concreta.
  • Mejora la consistencia entre productos: el uso de la factoría abstracta permite forzar a utilizar un conjunto de objetos de una misma familia.
  • Como inconveniente podemos decir que no siempre es fácil soportar nuevos tipos de productos si se tiene que extender la interfaz de la Factoría abstracta.

Factory Method

Libera al desarrollador sobre la forma correcta de crear objetos. Define la interfaz de creación de un cierto tipo de objeto, permitiendo que las subclases decidan que clase concreta necesitan instancias.
Muchas veces ocurre que una clase no puede anticipar el tipo de objetos que debe crear, ya que la jerarquía de clases que tiene requiere que deba delegar la responsabilidad a una subclase.

Este patrón debe ser utilizado cuando:
  • Una clase no puede anticipar el tipo de objeto que debe crear y quiere que sus subclases especifiquen dichos objetos.
  • Hay clases que delegan responsabilidades en una o varias subclases. Una aplicación es grande y compleja y posee muchos patrones creacionales. 
 Diagrama  UML.


Creator: declara el método de fabricación (creación), que devuelve un objeto de tipo Product.
ConcretCreator: redefine el método de fabricación para devolver un producto.
ProductoConcreto: es el resultado final. El creador se apoya en sus subclases para definir el método de fabricación que devuelve el objeto apropiado.


Ejemplo

En nuestro ejemplo tenemos una clase abstracta llamada Triangulo, de la cual heredan los 3 tipos de triangulos conocidos.





Pero tenemos el siguiente inconveniente: quien se encargue de crear un tipo de triángulo concreto no debería tener que conocer como se compone internamente.
Para ello, hemos creado la clase TrianguloFactory con su correspondiente interface.


Veremos que, desde el punto de vista del cliente, es muy sencillo poder crear un triángulo:

Consecuencias

  • Como ventaja se destaca que elimina la necesidad de introducir clases específicas en el código del creador. Solo maneja la interfaz Product, por lo que permite añadir cualquier clase ConcretProduct definida por el usuario.
  • Por otro lado, es más flexible crear un objeto con un Factory Method que directamente: un método factoría puede dar una implementación por defecto.


Temas a tener en cuenta.

Si bien he escrito sobre la teoría del patrón Factory, hay que decir que en la realidad este patrón es mucho más versátil. Con esto me refiero a que no siempre se trata de casos como el que vimos en el ejemplo (caso ideal), donde el factory se aplica a una jerarquía de subclases.
Muchas veces nos vamos a encontrar con casos donde no aplican ninguno de los patrones creacionales, pero aún así nos vamos a dar cuenta que necesitamos aliviarle la tarea a un cliente para que pueda crear un determinado objeto. A esta clase que le facilita la creación al cliente también se la suele denominar Factory, aún cuando no se trate de una jerarquía. Como consecuencia, podriamos redefinir el término del factory ajustándolo un poco a la realidad de la programación: se trata de aquella clase que intenta aliviar la carga de crear un objeto de manera correcta, cuando el resto de los patrones creacionales no aplican.

lunes, 30 de mayo de 2011

Prototype

El patrón prototype tiene un objetivo muy sencillo: crear a partir de un modelo.Permite crear objetos prediseñados sin conocer detalles de cómo crearlos. Esto lo logra especificando los prototipos de objetos a crear. Los nuevos objetos que se crearán de los prototipos, en realidad, son clonados. Vale decir, tiene como finalidad crear nuevos objetos duplicándolos, clonando una instancia creada previamente.

Cuando utilizar este patrón.

Aplica en un escenario donde sea necesario la creación de objetos parametrizados como "recién salidos de fábrica" ya listos para utilizarse, con la gran ventaja de la mejora de la performance: clonar objetos es más rápido que crearlos y luego setear cada valor en particular.
Este patrón debe ser utilizado cuando un sistema posea objetos con datos repetitivos, en cuanto a sus atributos: por ejemplo, si una biblioteca posee una gran cantidad de libros de una misma editorial, mismo idioma, etc. Hay que pensar en este patrón como si fuese un fábrica que tiene ciertas plantillas de ejemplos de sus prodcutos y, a partir de estos prototipos, puede crear una gran cantidad de productos con esas características.

Diagrama UML




Prototype: declara la interface del objeto que se clona. Suele ser una clase abstracta.
PrototypeConcreto: las clases en este papel implementan una operación por medio de la clonación de sí mismo.
Cliente: crea nuevos objetos pidiendo al prototipo que se clone.

Los objetos de Prototipo Concreto heredan de Prototype y de esta forma el patrón se asegura de que los objetos prototipo proporcionan un conjunto consistente de métodos para que los objetos clientes los utilicen.

Ejemplo

Nuestro ejemplo será muy sencillo: prototipos de TVs. Para ello crearemos la clase TV y dos subclases: LCD y Plasma. Ya debería ir cambiando Plasma por Led no?



Y, por otro lado, tenemos gestor de prototipos: TvPrototype.

La invocación desde un Cliente sería:


Consecuencias

  • Un programa puede dinámicamente añadir y borrar objetos prototipo en tiempo de ejecución. Esta es una ventaja que no ofrece ninguno de los otros patrones de creación.
  • Esconde los nombres de los productos específicos al cliente.
  • Se pueden especificar nuevos objetos prototipo variando los existentes.
  • La clase Cliente es independiente de las clases exactas de los objetos prototipo que utiliza. y, además, no necesita conocer los detalles de cómo construir los objetos prototipo.
  • Clonar un objeto es más rápido que crearlo.
  • Se desacopla la creación de las clases y se evita repetir la instanciación de objetos con parámetros repetitivos.

Otros temas a tener en cuenta.

Debido a que el patrón Prototye hace uso del método clone(), es necesaria una mínima explicación de su funcionamiento: todas las clases en Java heredan un método de la clase Object llamado clone. Un método clone de un objeto retorna una copia de ese objeto. Esto solamente se hace para instancias de clases que dan permiso para ser clonadas. Una clase da permiso para que su instancia sea clonada si, y solo si, ella implementa el interface Cloneable.
Por otro lado, es importante destacar que si va a variar el número de prototipos se puede utilizar un "administrador de prototipos". Otra opción muy utilizada es un Map como se ve en el ejemplo.

domingo, 29 de mayo de 2011

Builder

Permite la creación de un objeto complejo, a partir de una variedad de partes que contribuyen individualmente a la creación y ensamblación del objeto mencionado. Hace uso de la frase "divide y conquistarás". Por otro lado, centraliza el proceso de creación en un único punto, de tal forma que el mismo proceso de construcción pueda crear representaciones diferentes.

Los objetos que dependen de un algoritmo tendrán que cambiar cuando el algoritmo cambia. Por lo tanto, los algoritmos que estén expuestos a dicho cambio deberían ser separados, permitiendo de esta manera reutilizar dichos algoritmos para crear diferentes representaciones.

Se debe utilizar este patrón cuando sea necesario:
  • Independizar el algoritmo de creación de un objeto complejo de las partes que constituyen el objeto y cómo se ensamblan entre ellas.
  • Que el proceso de construcción permita distintas representaciones para el objeto construido, de manera dinámica.
Esta patrón debe utilizarse cuando el algoritmo para crear un objeto suele ser complejo e implica la interacción de otras partes independientes y una coreografía entre ellas para formar el ensamblaje. Por ejemplo: la construcción de un objeto Computadora, se compondrá de otros muchos objetos, como puede ser un objeto PlacaDeSonido, Procesador, PlacaDeVideo, Gabinete, Monitor, etc.

Diagrama UML


Producto: representa el objeto complejo a construir.
Builder: especifica una interface abstracta para la creación de las partes del Producto. Declara las operaciones necesarias para crear las partes de un objeto concreto.
ConcreteBuilder: implementa Builder y ensambla las partes que constituyen el objeto complejo.
Director: construye un objeto usando la interfaz Builder. Sólo debería ser necesario especificar su tipo y así poder reutilizar el mismo proceso para distintos tipos.

El Cliente crea el objeto Director y lo configura con el objeto Builder deseado.
El Director notifica al constructor cuándo una parte del Producto se debe construir.
El Builder maneja los requerimientos desde el Director y agrega partes al producto.
El Cliente recupera el Producto desde el constructor.

Ejemplo

Antes de meternos de lleno con el ejemplo, vale aclarar lo siguiente: muchas veces los patrones trabajan juntos. Por ejemplo, tiene mucho sentido que un objeto Director sea un Singleton. Pero para no llenar el código de confusión, hacerlo más legible y poner el foco en el patrón del momento, voy a obviar estos detalles.

El objetivo del ejemplo es poder crear un objeto Auto (este sería nuestro producto). El auto se compondrá de varios atributos que lo componen: motor, marca, modelo y cantidad de puertas. En nuestro ejemplo, el auto no se compone de muchos objetos complejos. De hecho, se compone de sólo 4 objetos relativamente sencillos. Esto es para poder hacer entendible la propuesta del Builder y no perderse en los objetos que lo componen. Queda en la imaginación del lector la posibilidad de trabajar con ejemplos más complejos. Yo particularmente usé mucho este patrón cuando trabajé con archvios.



Siguiendo con nuestro ejemplo, definimos nuestro Builder llamado AutoBuilder. El Builder define al menos dos cosas: un método para devolver el Producto (el auto en nuestro caso) y los métodos necesarios para la construcción del mismo.
Serán los ConcreteBuilders los encargados de colocarle la lógica de construcción de cada Auto en particular. En nuestro caso, tendremos dos ConcreteBuilder: FiatBuildery FordBuilder. Recordemos que, en nuestro ejemplo, son clases que construyen objetos muy sencillos con datos hardcodeados para facilitar el aprendijaze del patrón en sí.



Por último, realizaremos el Director. Lo primero que debe hacerse con esta clase es enviarle el tipo de auto que se busca construir (Ford, Fiat, etc). Luego, al llamar al método constructAuto(), la construcción se realizará de manera automática.


La invocación desde un cliente sería:

Consecuencias
  • Permite variar la representación interna de un producto.
  • El Builder ofrece una interfaz al Director para construir un producto y encapsula la representación interna del producto y cómo se juntan sus partes.
  • Si se cambia la representación interna basta con crear otro Builder que respete la interfaz.
  • Separa el código de construcción del de representación.
  • Las clases que definen la representación interna del producto no aparecen en la interfaz del Builder.
  • Cada ConcreteBuilder contiene el código para crear y juntar una clase específica de producto.
  • Distintos Directores pueden usar un mismo ConcreteBuilder.
  • Da mayor control en el proceso de construcción.
  • Permite que el Director controle la construcción de un producto paso a paso.
  • Sólo cuando el producto está acabado lo recupera el director del builder.

jueves, 26 de mayo de 2011

Singleton

La idea del patrón Singleton es proveer un mecanismo para limitar el número de instancias de una clase. Por lo tanto el mismo objeto es siempre compartido por distintas partes del código. Puede ser visto como una solución más elegante para una variable global porque los datos son abstraídos por detrás de la interfaz que publica la clase singleton.
Dicho de otra manera, esta patrón busca garantizar que una clase sólo tenga una instancia y proporcionar un punto de acceso global a ella.

Usaremos este patrón cuando:
♦ Debe haber exactamente una instancia de una clase y deba ser accesible a los clientes desde un punto de acceso conocido.
♦ Se requiere de un acceso estandarizado y conocido públicamente.

Sus usos más comunes son clases que representan objetos unívocos. Por ejemplo, si hay un servidor que necesita ser representado mediante un objeto, este debería ser único, es decir, debería existir una sola instancia y el resto de las clases deberían de comunicarse con el mismo servidor. Un Calendario, por ejemplo, también es único para todos.
No debe utilizarse cuando una clase esta representando a un objeto que no es único, por ejemplo, la clase Persona no debería ser Singleton, ya que representa a una persona real y cada persona tiene su propio nombre, edad, domicilio, DNI, etc.


Diagrama UML
En el diagrama, la clase que es Singleton define una instancia para que los clientes puedan accederla. Esta instancia es accedida mediante un método de clase.
Los clientes (quienes quieren acceder a la clase Singleton) acceden a la única instancia mediante un método llamado getInstance().

Ejemplo.

Como dije anteriormente, este patrón es ideal para aquellas clases que representan objetos únicos. Por ejemplo, un instituto educativo es un objeto único. No deberíamos crear muchas instancias de esta clase ya que al hacer esto estaríamos diciendo que hay varios institutos educativos. Caso contrario serían los alumnos que asisten a dicho instituto. Debería haber un objeto por cada uno de los alumnos, ya que todos ellos tienen propiedades distintivas, desde el nombre hasta el documento de identidad. Pero todos los alumnos deberían comunicarse con el mismo instituto.

Entonces, haremos que el instituto aplique el patrón Singleton:


Para llamar al instituto debemos hacer lo siguiente:




Bien, veamos ahora este mismo ejemplo, con una pequeña modificación: vamos a agregarle un atributo a la clase InstitutoEducativo y veremos como se comporta a lo largo de las distintas llamadas al método getInstance().

Y, el main, que representa al cliente que necesita utilizar esta clase:




Otros temas a considerar.
 
Como vimos, el Singleton es un patrón sencillo para aplicar. Solo requiere de unos pequeños cambios a una clase. Sin embargo, debemos considerar un tema importante con respecto a este patrón: ¿que pasa si dos hilos del programa llaman (la primera vez) al método getInstance() al mismo tiempo? Bueno aqui podriamos tener un problema, ya que existe la remota posibilidad de que se logre crear dos instancias de la clase, en vez de una como quisieramos. La solución más sencilla es realizar un pequeño cambio:



¿Hay más soluciones para que el Singleton sea Thread Safe? Si, podemos ver algunas de ellas en http://en.wikipedia.org/wiki/Singleton_pattern