jueves, 28 de octubre de 2010

Patrones de diseño: Template Method

Estre patrón define el funcionamiento general de un algoritmo en una operación de una clase, delegando en otras clases, a través de herencia o dependencia entre clases, la implementación específica de cada uno de sus pasos. Permite que estas clases redefinan ciertos pasos del algoritmo sin cambiar su comportamiento general, adaptandose cada una de ellas a una necesidad específica.

Una implementación clásica de este patrón es la que aparece en el libro “Design Patterns” del GOF, que presenta una clase abstracta en la que se define el comportamiento general de una operación en un método concreto que llama a su vez a otros métodos abstractos en los que se definen comportamientos más específicos. Distintas subclases pueden heredar de esta clase y especificar comportamientos diferentes para los métodos abstractos, permitiendo varias implementaciones que se puedan ajustar a cada necesidad.

El siguiente diagrama UML presenta un ejemplo basado en esta forma de implementación del patrón Template Method:

En él, una clase Juego define en su método jugar() el comportamiento general de cualquier juego, el juego comienza y cada uno de los jugadores va haciendo su jugada hasta que se cumple una condición que da fin al juego y se muestra el resultado. Estas operaciones se indican mediante métodos abstractos que deben ser implementados por cada juego específico de forma apropiada, como pueden ser el Parchis o el Ajedrez.

Sin embargo, al ser la herencia la forma de acoplamiento más fuerte que existe entre clases, en busca de una mayor flexibilidad se propone una implementación alternativa basada en la dependencia entre clases en lugar de la herencia.

El siguiente diagrama muestra un ejemplo de implementación del patrón Template Method usando la dependencia entre clases:

En él se muestra una clase plantilla JuegoTemplate con un método jugar() que define el comportamiento general de cualquier juego. Igual que antes, el juego comienza y cada uno de los jugadores va haciendo su jugada hasta que se cumple una condición que da fin al juego y se muestra el resultado. Estas operaciones son llevadas a cabo por una instancia de una clase que implementa la interfaz Juego, interfaz que especifica los métodosque deben ser implementados por cada juego específico. Las clases Parchis y Ajedrez implementan la interfaz Juego y definen el comportamiento específico de estos juegos.

Esta forma de implementar el patrón Template Method es el usado en Spring para proporcionar, por ejemplo, soporte en el acceso a JDBC o Hibernate, a través de sus plantillas JDBCTemplate o HibernateTemplate, que usan una instancia de una clase que implementa la interfaz DataSource y que encapsula el comportamiento específico de acceso a una fuente de datos.

Referencias:
Design Patterns (Erich Gamma, Richard Helm, Ralph Johnson, Hohn Vlissides)
Patrón Template Method en Wikipedia
Implementacion de pattern Template utilizando composición e inyección
The Template Design Pattern
Patrón Template Method
Otros patrones de diseño

9 comentarios:

  1. Muy interesante y muy bien escrito y explicado, como siempre. Me cuesta bastante imaginar ejemplos prácticos en los que la segunda implementación sea preferible a la primera. Limitado que es uno jejeje :-D

    Sin embargo, a menos que me "alumbres" con un ejemplo, seguiré prefiriendo la primera opción por ser más fácil de comprender por quien la viese.

    Este blog se está convirtiendo en una referencia muy buena de patrones de diseño en castellano (y más cosas). Enhorabuena yo estoy aprendiendo bastantes cosas siguíendote.

    Un saludete

    ResponderEliminar
  2. ¡Muy buenas! Ya te estaba esperando...

    Pues no se trata de que una sea mejor que la otra porque sí, sino que una puede ser mejor que la otra en un determinado contexto.

    Como dices, la primera opción es más simple a primera vista, y correcta. En cambio, Spring usa la segunda opción en el acceso a base de datos... ¿Por qué? Porque para ellos prima el poder gestionar las dependencias de un objeto a través de su contenedor de DI.

    La segunda opción también te permite hacer tests unitarios del algoritmo que está implementado en la clase JuegoTemplate inyectándole una implementación de Juego de prueba sin tener que implementar un juego en concreto. El ejemplo es sencillo, pero para casos en los que haga falta probar un algoritmo complicado y crear una implementación concreta de la interfaz sea costoso, el poder usar una implementación de prueba puede facilitar el desarrollo.

    Espero haberme explicado, aunque tampoco se trata de sentar cátedra. El escribir ésto sólo lo hago para poder discutir sobre ello con gente como tú, y seguir aprendiendo.

    Gracias por tus comentarios.

    ResponderEliminar
  3. Dices: La segunda opción también te permite hacer tests unitarios del algoritmo que está implementado en la clase JuegoTemplate inyectándole una implementación de Juego de prueba sin tener que implementar un juego en concreto.

    Digo: Sigo sin ver la diferencia entre hacer un test unitario para probar el método "jugar()" directamente tirando de una clase sencilla que herede de Juego y hacer ese mismo test de la clase usando la clase JuegoTemplate y una clase sencilla que implemente Juego. En ambos casos tienes que hacer una clase sencilla que implemente una serie de métodos. Símplemente no me entra en la cabeza jeje

    No dudo ni un instante que hayan ventajas a efectos prácticos y evidentemente lo que Spring dice va a misa, por lo menos comparándolos con mis conocimientos son Dios jejeje

    Por cierto, y no viene a cuento para nada, me suena, y digo me suena por que nunca he profundizado, que había una fuerte corriente crítica precisamente hacia la forma de funcionar de HibernateTemplate o algo así. ¿Tiene algo que ver con esto?

    ResponderEliminar
  4. Tienes razón, pero lo que estás probando es la clase de prueba, clase que vas a deshechar por una real, que no está probada y para la que habría que repetir las pruebas. De todas maneras, esto depende de hasta donde se quiera llegar con los tests y la inyección de dependencias.

    No tengo ni idea de esa corriente crítica con HibernateTemplate que mencionas. Ya echaré un vistazo en Google a ver si encuentro algo y a qué se debe.

    Un saludo.

    ResponderEliminar
  5. Bueno, realmente lo que pruebas son los métodos que están en la clase abstracta, el tema es que para probarlos necesitas una implementación. En el otro caso lo que pruebas son los métodos equivalentes de la clase Template.

    Digamos que para hacer unas pruebas 100% académicas y correctas acepto el segundo planteamiento pero aún me sigue pesando más el tema de la simplicidad.

    También por el tema del acoplamiento. Básicamente cualquier cambio en un método de la clase Template no afecta a ninguna clase más por que ninguna hereda de esta, mientras que, con la primera forma de aplicar el patrón, cualquier cambio afecta a las clases que lo implementan.

    Supongo que aquí está el quid de la cuestión. Si quieres construir un sistema más orientado a "enchufar" cosas (digamos "componentes") cómo es Spring, entiendo que te interese esta forma de implementar el patrón. Sobretodo si es de cara a "enchufar" cosas hechas por terceros. Mientras menos mano se pueda meter ...

    Por eso te decía que no le veo una ventaja ya que a mi el acoplamiento me preocupa siempre hasta cierto punto, también te digo que yo no soy programador de SpringSource :-)

    Un saludo

    ResponderEliminar
  6. Ahí estamos de acuerdo, en el "quid" de la cuestión. Dependiendo de la naturaleza del sistema que estás construyendo te interesa más una u otra implementación.

    La primera es más simple, no hay discusión. Además es la implementación clásica del libro Design Patterns del GOF. La otra es más "tipo Spring", y aunque a la hora de implementar este patrón uno tiende a hacerlo de la forma más sencilla, es interesante conocer la visión de Spring, sobre todo cuando uno lo está usando en sus aplicaciones...

    Aunque no seamos programadores de Springsource... no saben nada los tíos...

    ResponderEliminar
  7. Jorge, necesito entender la diferencia entre patron strategy y template method, partiendo de un diagrama de clases(Modelo del dominio del problema). Pls si tuvieras ejericios practicos que me iluminen t voy a agradecer todo la vida. Rindo este sabado 06/11.

    ResponderEliminar
  8. Hola Susan,

    La diferencia está en que el patrón Template Method te permite tener distintas implementaciones del mismo algoritmo, mientras que el patrón Strategy permite manejar distintos algoritmos de forma consistente.

    Como estoy tratando de revisar todos los patrones que puedo, voy a tratar de escribir un post sobre el patrón Strategy, aunque no te prometo nada, dependerá del tiempo que logre sacar...

    Un saludo.

    ResponderEliminar
  9. Al final sí he tenido tiempo para el post sobre el patrón Strategy:

    http://tratandodeentenderlo.blogspot.com/2010/11/patrones-de-diseno-strategy.html

    ResponderEliminar