Java Design Patterns – Tutorial con ejemplos

[Fuente: http://www.journaldev.com/1827/]

java-design-patternsLos patrones de diseño son muy populares entre los programadores de software.

  • Un patrón de diseño es una solución bien descrita a un problema común de software.
  • Algunos de los beneficios de utilizar patrones de diseño son:
    • Los patrones de diseño están ya definidos y proporcionados como estándares por la industria para resolver problemas recurrentes, de forma que ahora mucho tiempo.
    • Utilizar patrones de diseño promociona reusabilidad lo cual produce un código más mantenible , robusto y reusable. Ayuda a reducir el total cost of ownership (TCO) of the software product.
    • Ya que los patrones de diseño están ya definidos, hace nuestro código más fácil de entender y depurar. Esto lleva a un desarrollo más rápido y a que si se incorporan nuevos miembros a un equipo les sea fácil entender el código.
  • Los patrones de diseño Java se dividen en tres categorías: creacionales (creational) , estructurales (structural) y de comportamiento (behavioral).En este post haremos un índice a artículos sobre patrones de diseño publicados en otros post.

Creational Design Patterns

Los patrones de diseño creacionales proporcionan una solución para instanciar un objeto de la mejor manera posible en situaciones específicas.

Singleton

El patrón singleton restringe la instanciación de una clase de forma que asegura que solo una instancia de la clase existe en la JVM. Parece un patrón de diseño muy simple pero cuando hay que implementarlo , viene con varios aspectos de implementación a considerar. La implementación de un patrón Singleton supone siempre un tema controvertido entre los programadores. Visita Singleton Design Pattern para aprender más sobre las diferentes formas de implementar este patrón y los pros y las contras de cada uno de los métodos.

Factory

El patrón Factory se utiliza cuando tenemos una super clase con múltiples subclasses y , basándose en la entrada del usuario , necesitamos retornar una de las subclases. Este patrón le da la responsabilidad de la instanciación de una clase del programa cliente a la clase factory. Podemos aplicar el patrón Singleton en la clase Factory o hacer el método factory estático. Visita Factory Design Pattern para ver un ejemplo de un programa y los beneficios de este patrón.

Abstract Factory

El patrón Abstract Factory es similar al patrón Factory ya que es como una factoria de factorias. Si ya conoces el patrón factory en Java , te darás cuenta que tenemos una sola Factory class que retorna las diferentes subclases basandose en la entrada del usuario. La factory class utiliza if-else o la sentencia switch para conseguir esto.

En el patrón Abstract Factory , evitamos los bloques if-else y lo que se hace es tener una factory class por cada subclase y entonces una clase Abstract Factory que retornará la subclase basándose en la factory class de entrada. Visita Abstract Factory Pattern  para conocer cómo se implementa con un programa de ejemplo.

Builder 

Este patrón fue introducido para resolver algunos de los problemas con Factory y Abstract Factory cuando el Object contiene muchos atributos. El patrón Builder resuelve el problema de tener un grán número de parámetros opcionales y estado inconsistente proporcionando una forma de construir el objeto paso a paso y proporcionar un método que retornará el objeto final. Visita Builder Pattern para ver un programa de ejemplo y las clases utilizadas en el JDK.

Prototype 

El patrón Prototipo se utiliza cuando la creación del objeto es un asunto de mucho coste y requiere mucho tiempo y recursos y además tienes un objeto similar ya existente. Así este patrón proporciona un mecanismo para copiar el objeto original a un nuevo objeto y entonces modificarlo de acuerdo a nuestras necesidades. Este patrón utilizar java cloning para copiar el objeto.

Este patrón de diseño obliga a que el objeto que estás copiando debe proporcionar un método de copiado. El copiado no debe ser hecho por otra clase. Sin embargo sobre si utilizar shallow copy o deep copy de las properties del Object depende de los requerimientos y es una decisión de diseño. Visita Prototype Pattern para ver un programa de ejemplo.

Structural Design Patterns

Los patrones estructurales proporcionan distintas formas de crear una estructura de clase, por ejemplo utilizar herencia y composición para crear un objeto grande de objetos pequeños.

Adapter 

El patrón Adapter es utilizado para cuando dos interfaces sin relación tienen que trabajar juntos. El objeto que une estos interfaces se le llama un Adapter. Como ejemplo de la vida real, podemos pensar en un cargador de móvil como un adapter porque la batería del móvil necesita 3 voltios para cargar pero el enchufe normal tiene 120 voltios de salida (EEUU) y 240V (Europa y la India). Asi que el cargador de un móvil funciona como un adapter entre el conector de carga del móvil y el enchufe de la pared. Visita Adapter Pattern  para ver un programa de ejemplo.

Composite 

El patrón composite es uno de los patrones estructurales y es utilizado cuando tenemos que representar una jerarquía part-whole. Cuando tenemos que crear una estructura en la forma de que los objetos de la estructura deben ser tratados de la misma forma, podemos aplicar este patrón.

Lo entenderemos con un ejemplo de la vida real – Un diagrama es una estructura que se compone de Objects como Circle, Lines, Triangles etc y cuando coloreamos (por ejemplo de Red), el mismo color debe ser aplicado a todos los Objects del dibujo. Aquí dibujar es hecho de distintas partes y todos ellos tienen las mismas operaciones. Visita Composite Pattern  para ver distintos componentes de composite y un programa de ejemplo.

Proxy 

Este patrón pretende “Proporcionar un sustituto de otro objeto para controlar el acceso a este“. La definición en si misma es muy clara de forma que se usa este patrón cuando queremos proporcionar acceso controlado de una funcionalidad.

Digamos que tenemos una clase que puede ejecuta algún comando en el sistema. Ahora si estamos utilizándolo, imaginemos que queremos dar este programa a una aplicación cliente, puede haber entonces varios problemas gordos cuando el programa cliente puede facilitar comandos para borrar ficheros del sistema o cambiar algunas settings que no son deseables. Visita Proxy Pattern para ver más detalles.

Flyweight 

Este patrón es utilizado cuando necesitamos crear varios Objects de una clase. Ya que cada objeto consume espacio de memoria que puede ser crucial para dispositivos con pocos recursos de memoria, como por ejemplo dispositivos móviles, el patrón flyweight puede ser apli Flyweight Pattern .

Facade 

El patrón Facade es utilizado para ayudar a las aplicaciones cliente interactuen fácilmente con el sistema. Supongamos que tenemos una aplicación con un conjunto de interfaces para utilizar bases de datos Mysql / Oracle y generar distintos tipos de informes, como por ejemplo un informe HTML , un PDF , etc. Asi tenemos varis conjuntos de interfaces con distintos tipos de bases de datos. Ahora una aplicación cliente puede utilizar estos interfaces para pedir conexión a la base de datos y generar informes. Pero cuando la complejidad se incrementa o los nombres de los interfaces son confusos, la aplicación cliente encontrará difícil utilizarlos. Asi podemos aplicar el patrón Facade para un proporcionar un wrapper interface encima del interface existente para ayudar a la aplicación cliente. Visita el Facade Pattern para ver detalles de implementación y programa de ejemplo.

Bridge 

Cuando tenemos jerarquías de interfaces tanto en interfaces como en implementaciones, entonces el patrón Builder se utiliza para desacoplar los interfaces de la implementación y esconder los detalles de implementación a los programas cliente. Como el patrón Adapter, es uno de los patrones estructurales.La implementación del patrón Bridge sigue la noción de preferir Composition sobre la herencia. Visita Bridge Pattern para más detalles de implementación y un programa de ejemplo.

Decorator 

El patrón Decorator se utiliza para modificar la funcionalidad de un objeto en tiempo de ejecución. Al mismo tiempo otras instancias de la misma clase no serán afectados por esto, así que el objeto individual condigue realizar el comportamiento modificado. El patrón Decorator es uno de los patrones estructurales y utiliza las abstract classes o los interfaces con la composition para ser implementado.

Utilizamos herencia o composition para extender el comportamiento de un objeto pero esto es hecho en tiempo de compilación y es aplicable a todas las instancias de la clase. No podemos añadir ninguna nueva funcionalidad o borrar alguna existente en tiempo de ejecución – aqui es donde el patrón Decorator entra en escena. Visita Decorator Pattern para ver un ejemplo de programa y detalles de implementación.

Behavioral Design Patterns

Los patrones de comportamiento proporcionan solución para que haya mejor interacción entre los objetos y como proporcionar poco acople y flexibilidad se haga de forma fácil.

Template Method

  • El template Method es utilizado para crear un método stub y delegar algunos de los pasos de la implementación a las subclases. El Template method define los pasos para ejecutar un algoritmo y puede proporcionar una implementación por defecto que podría ser común a todas o a algunas de las subclases.

Supongamos que queremos proporcionar un algoritmo para construir una casa. Los pasos necesarios a ser realizados para construir una casa son  – construir el diseño , construir los pilares , construir los muros y las ventanas. El punto importante es que no podemos cambiar el orden de ejecución porque no podemos poner las ventanas antes de construir la base. asi que en este caso podemos crear un template method que usará distintos métodos para construir la casa. Visita Template Method Pattern para ver más detalles de implementación.

Mediator 

  • El patrón Mediator se utiliza para proporcionar un medio de comunicación centralizada entre distintos objetos en un sistema. Estos patrones de diseño son muy útiles cuando en una aplicación corporativa hay varios objetos que están interactuando los unos con los otros. Si los objetos interactuan entre ellos directamente, los componentes del sistema quedan muy acoplados entre llos lo que hace que la mantenibilidad sea costosa y muy poco flexible de extender. El patrón Mediator se enfoca en proporcionar un mediator entre los objetos para que se comuniquen y les ayuda a tener poco acoplamiento entre ellos.

Los controladores aereos son un grans ejemplo de mediator donde la torre de control funciona como un mediator para que exista comunicación entre los distintos vuelos. Mediator funciona como un router entre objetos y puede tener su propia lógica para proporcionar una forma de comunicación determinada. Visita Mediator Pattern para más detalles.

Chain of Responsibility 

Este patrón se utiliza para conseguir desacople en el diseño de un software donde una request de un cliente es pasada a una cadena de objetos para que sea procesada. Entonces el objeto de la cadena decide cada uno por si mismo quien procesará la request y si la request requiere ser enviada al siguiente objeto de la cadena o no.

Sabemos que tenemos varios catch blocks en un bloque de código try-catch. Aquí cada bloque catch es como un procesador que procesa una excepción en particular. así cuando cualquier excepción ocurre en el bloque try, es enviada al promer bloque catch para que sea procesada. Si ese catch block no puede procesarla, la pasa la siguiente objeto de la cadena, es decir el siguiente bloque catch. Si incluso el último bloque catch de la cadena no puede procesar la excepción, la excepción es enviada fuera de la cadena del programa que invoca.

Los cajeros automáticos (ATM) pueden ser implementadas utilizando Chain of Responsibility Pattern, visita ese link para más detalles.

Observer 

El patrón Observer es útil cuando estás interesado en el estado de un objeto y queremos ser notificados cuando haya algún cambio. En este patrón , el objeto que observa el estado de otro objeto es llamado el Observer y el objeto siendo observado el Subject.

Java proporciona en su core la implementación de Observer a través de la clase java.util.Observable y el interfaz java.util.Observer.Sin embargo no es ampliamente utilizado porque la implementación es muy simple y la mayoría de las veces no queremos finalizar ampliando una clase solo para implementar el patrón Observer ya que java no proporciona herencia múltiple en las clases.

El  Java Message Service (JMS) utiliza Observer junto con el patrón Mediator para permitir a las aplicaciones suscribirse y publicar datos en otras aplicaciones. Visita Observer Pattern para más detalles y un ejemplo de implementación.

Strategy 

El patrón Strategy se utiliza cuando tenemos varios algoritmos para realizar una tarea específica y el cliente decide qué implementación va a ser utilizada en tiempo de ejecución.

El patrón Strategy también es conocido como el patrón Policy. Definimos varios algoritmos y permite a una aplicación cliente pasar el algoritmo a ser utilizado como un parámetro. Uno de los mejores ejemplos de este patrón es el método Collections.sort() que recibe el parámetro Comparator. Basándose en las implementaciones distintas de los interfaces Comparator , los objetos son ordenados de distintas formas. Visita Strategy Pattern para más detalles.

Command 

Este patrón se utiliza para implementar desacople en un modelo de request-response. En este patrón, la request se envía al invocador y es el invocador el que la pasa al objeto Command encapsulado. El objeto Command pasa la request al método apropiado del Receiver para realizar la acción específica.

Digamos quee queremos proporcionar una utilizada de sistema de ficheros con métodos para abrir , escribir y cerrar ficheros y que debe funcionar para múltiples sistemas operativos tales como Windows o unix.

Para implementar nuestra utilidad de File System , lo primero de todo que necesitamos es crear las clases receiver que harán todo el trabajo. Ya que codificamos en términos de interfaces java, podemos tener un interfaz FileSystemReceiver y sus clases de implementación para distintos sistemas operativos. Visita  Command Pattern para más detalles.

State 

El patrón de State se utiliza cuando un objeto cambia su comportamiento basándose en su estado interno.

Si tenemos que cambiar el comportamiento de un objeto basándose en su estado , podemos tener una variable de estado en el Object y utilizar un bloque de condiciones if-else para realizar diferentes acciones basadas en el estado. Este patrón se utiliza para proporcionar un forma sistematica y desacoplada de conseguirlo a través de las implementaciones Context t State. Visita  State Pattern para más detalles.

Visitor

Este patrón se utiliza cuando tenemos que realizar una operación sobre un grupo similar de objects. Con la ayuda del patrón Visitor, podemos mover la lógica operacional desde los objetos a otra clase.

Por ejemplo, pensemos de un carrito de la compra donde podemos añadir distintos tipos de productos (Elements) , cuando clicamos en el botón de comprar,  calcula la cantidad total a ser pagada. Ahora podemos tener la lógica de cálculo en las clases que modelan los productos o podemos mover está lógica a otra clase utilizando el patrón Visitor. Veamos ejemplo en  Visitor Pattern para más detalles de implementación.

Interpreter 

Se utiliza para definir una representación gramátical para un lenguaje y proporciona un interprete para tratar con esta gramática.

El mejor ejemplo de esta patrón es un java compiler que interpreta el código fuente java en byte code entendible por la JVM. Google Translator es también un ejemplo de interprete donde la entrada puede ser en cualquier lenguaje y podemos dar la salida interpretada en otro lenguaje. Visita Interpreter Pattern

Iterator

Es utilizado para proporcionar una forma estandar para recorrer un grupo de objetos. Este patrón se utiliza bastante en las  Java Collection Framework donde el Iterator interfaz proporciona métodos para recorridos a través de una colección.

El patrón Iterator no es sólo sobre recorrer colecciones , podemos proporcionar distintos tipos de iterator basándonos en nuestros requerimientos.El patrón Iterator esconde la implementación actual de recorrido a través de una colección y los programas cliente sólo utilizan los métodos del iterator. Visita Iterator Pattern.

Memento 

Este patrón se utiliza cuando queremos salvar el estado de un objeto de forma que podamos recuperarlo más tarde. Este patrón implementa esta solución de tal forma que el estado grabado del objeto no es accesible fuera del objeto, esto protege la integridad de los datos de estado guardados.

Se implementa con dos objetos –  Originator y Caretaker. Originator es el objeto del cual necesitamos su estado para ser grabado y recuperado y que utiliza una clase inner para guardar el estado de un Object. Esta inner class es llamada Memento y es privada , asi que no puede ser accedida desde otros objetos. Visita Memento Pattern.