Todos conocemos el patrón de diseño Template que permite que alguna parte de un algoritmo sea definido y pueda ser implementado en una clase base definiendo un comportamiento genérico de modo que las diferentes implementaciones definan un comportamiento específico para completar la tarea del algoritmo. De esta manera, las subclases pueden sobrescribir métodos de forma de darle significado a un algoritmo sin cambiar la estructura general de este. Es particularmente usado para separar el comportamiento variante del invariante, minimizando la cantidad de código escrito. El comportamiento invariante es codificado en la clase abstracta (template) a entonces cualquier subclase que herede de esta puede sobrescribir los métodos abstractos e implementan las necesidades especificas del contexto. (1)
En el libro GOF se realiza la siguiente definición: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. (2)
El problema con este modelo es que la herencia a un tipo de relación entre objetos que es muy acoplado, haciendo que el diseño sea poco flexible de modo que aca se plantea utilizar un modelo por composición para reemplazar la herencia. De esta manera se obtiene un diseño más flexible dado que se obtiene un modelo donde se pueden intercambiar (a través de la inyección) las partes específicas de un algoritmo.
Veamos como se vería un modelo de diseño aplicando estos principios. En primer lugar en tendríamos una clase que haga de template tal como el patrón en su forma original, por ejemplo:
public class Processor {
private Collaborator1 coll1;
private Collaborator2 coll2;
public Processor(Collaborator1 c1, Collaborator2 c2) {
this.coll1 = c1;
this.coll2 = c2;
}
/**
* Define el comportamiento genérico del algoritmo
* El comportamiento especifico de este algoritmo esta desarrollado en
* las implementaciones de los colaboradores
*/
public String process() {
// ejecuta alguna tarea ...
long id = coll2.getId();
coll1.doIt(id);
// realiza otra tarea ...
String result = coll2.execute();
// procesa el resultado
return result;
}
}
public interface Collaborator1 {
.
public void doIt(long cod);
}
public interface Collaborator2 {
public long getId();
public String execute();
}
De esta simple manera se obtiene la implementación del patrón template utilizando composición de objetos en lugar de herencia. Como vemos aquí se pueden reemplazar/combinar fácilmente, utilizando inyección, las implementaciones de los colaboradores que son los que tienen el código especifico del algoritmo. En este caso en particular se podrían crear diferentes instancias de la clase
Processor
para obtener diferentes implementaciones del algoritmo genérico. Incluso se podrían inyectar mock objects para los test unitarios del sistema.Por otro lado, seria recomendable utilizar algún IoC container para realizar la inyección de los colaboradores.
Si quisiéramos hacer esto mismo utilizando el patrón template con herencia, tendríamos la clase abstracta Processor, donde si método process se vería así:
public String process() {
// ejecuta alguna tarea ...
long id = this.getId();
this.doIt(id);
// realiza otra tarea ...
String result = this.execute();
// procesa el resultado
return result;
}
public abstract long getId();
public abstract void doIt(long cod);
public abstract String execute();
Y ahora tendríamos una nueva clase que herede de esta donde se implementen los métodos específicos del algoritmo. Como se puede queda un modelo más acoplado y menos flexible.
Aunque yo recomiendo utilizar esta técnica para el patrón template creo que hay que evaluar en cada caso lo que más conviene y queda mejor para un determinado caso.
(1) The Template Design Pattern
(2) Design Patterns - Elements of reusable Object-Oriented Software (GOF book)
No hay comentarios:
Publicar un comentario