(PHP5) Patrón de Diseño: Template Method (revisado: 7/11/2006)

La intención general de muchos patrones es contener el "foco de cambio", el lugar donde podrían originarse cambios en nuestro sistema, lo que requeriría tener que modificar código del mismo. Si "controlamos" ese foco, lograremos cumplir el principio "Open/Closed" (Abierto/Cerrado) que dice que: "nuestros sistemas deberían ser cerrados al cambio pero abiertos a la extensión". En otras palabras, nuestro diseño debería proveer la posibilidad de que nuestro sistema pueda crecer agregando nuevo código sin alterar el código existente.

Cuando nos acostumbremos a implementar patrones, nos daremos cuenta que la mayoría de los diseños cumplen con este principio.

En el caso concreto del patrón "Template Method", su intención es controlar el esqueleto de un algoritmo, permitiendo ofrecer un comportamiento base que pueda ser modificado al redefinir los métodos en las clases derivadas de la "Clase Padre" en el contexto de la herencia.

El gran beneficio de este patrón es la flexibilidad para aplicar cambios y para reusar código, facilitando además la alteración del "algoritmo base" de la solución que estamos implementando. El algoritmo está implementado en un único método y si debemos modificarlo este cambio impactará en todo el sistema de forma controlada, sin tener que modificar individualmente a las clases concretas (donde se encuentra la implementación de bajo nivel). Y sobre el caso de reuso, la "clase padre" de la herencia puede contener un "comportamiento por defecto" que todas las "clases hijas" van a heredar, y solo sobreescribirán el método correspondiente cuando necesiten cambiar su comportamiento según nuestros requerimientos.

Contexto del ejemplo

Necesitamos una estructura que nos permita generar listados que cumplen el siguiente algoritmo: imprimir primero la información del cabezal, luego el cuerpo y finalmente el pie del documento. Cada clase derivada vuelve a implementar el método que hereda de la "clase padre" cada vez que necesite cambiar el comportamiento base. De la misma forma, si deseamos agregar un nuevo tipo de listado, no necesitaremos modificar el código de nuestra solución, solo crear una clase y heredar de la clase padre ("abierto a la extensión").

Por demás está decir que el "foco de cambio" es claro: si tenemos un sistema que provee listados de distinto tipo, es muy probable que con el tiempo surjan modificaciones en el comportamiento general (cambia la estructura o el diseño de nuestra hoja de impresión) o se agregan nuevos casos a los existentes (un nuevo tipo de listado).

Se creó una clase Listado que será la única estructura que contendrá verdaderamente el algoritmo de la solución (dentro del método imprimir) y una serie de métodos que pertenecen *solo* a la lógica interna del algoritmo.


// Archivo: Listado.class.php

class Listado {

// Algoritmo base
public function imprimir(){
$this->cabezal();
$this->cuerpo();
$this->pie();
}
// Contenido inicial para cada parte del
// algoritmo

protected function cabezal(){
echo
"Listado - Cabezal";
}
protected function cuerpo(){
echo
"Listado - Cuerpo";
}
protected function pie(){
echo
"Listado - Pie";
}
}
?>

// Archivo: Comun.class.php

require_once 'Listado.class.php';

class
Comun extends Listado{

// Altero el comportamiento base
// de una parte del algoritmo, el "pié"

protected function pie(){
echo
"Comun - pie";
}

}
?>

// Archivo: Avanzado.class.php

require_once 'Listado.class.php';

class
Avanzado extends Listado{

// En este caso, modifico completamente
// el contenido de los elementos que
// comprenden el algoritmo, pero no
// quiere decir que pueda cambiar
// el orden del mismo (esto se mantiene
// controlado en la clase base "Listado")

protected function cabezal(){
echo
"Avanzado - cabezal";
}
protected function cuerpo(){
echo
"Avanzado - cuerpo";
}
protected function pie(){
echo
"Avanzado - pie";
}
}

?>

// Archivo: Estandar.class.php

require_once 'Listado.class.php';

class
Estandar extends Listado {
// En este caso, no hacemos nada
// y se reaprovecha todo el contenido
// de la "clase base"
}
?>

// Archivo: index.php

require_once 'Listado.class.php';
require_once
'Comun.class.php';
require_once
'Avanzado.class.php';
require_once
'Estandar.class.php';

abstract class Index {
public static function run(){
self::imprimir(new Comun());

self::imprimir(new Avanzado());

self::imprimir(new Estandar());
}
// Solo pueden pasar como parámetros
// objetos que sean del tipo "clase base",
// logrando efectuar un "contrato de diseño"
// (para poder imprimir debes heredar de la
// clase "Listado").
private static function imprimir(Listado $b){
$b->imprimir();
}
}

Index::run();
?>



Detalles a tener en cuenta

La definición del diseño del patrón dice que los métodos que se utilizan son parte del algoritmo y están con la visibilidad "protegida" y no "privada". Si fuera así, los métodos no podrían ser accesibles desde otras clase de la jerarquía, impidiendo que puedan sobreescribirse.

Si fuera "públicos" los métodos, podrían ser accedidos directamente desde la clase "cliente" (en este caso "Index") y acceder a partes del algoritmo, lo cual solo nos interesa que lo ejecuten de forma integra. Para este último caso está disponible el método "imprimir" de la clase "Listado".

Y como último detalle, no hace falta instanciar clases que no se necesitan o no deban hacerlo ... para eso se define clases "abstractas" que impiden la creación de instancias y a los métodos como "estáticos", lo que impide que puedan ser usados en el contexto de una instancia.

Dudas, sugerencias, correcciones, insultos, en los comentarios, plis ;-)

Más información

Wikipedia

Do Factory

11 comentarios:

Anónimo dijo...

Excelente post, excelente ejemplo, excelente explicación.

Enrique Place dijo...

Bueno, me faltó agregar el diagrama de secuencia y el de colaboración. ;-)

Siempre digo que a veces tengo facilidad para explicar cosas complejas porque logro bajarlas al nivel de la persona que no sabe ;-)

Esto se da por el simple hecho de que si eres "experto" en un área, pero estás acostumbrado a investigar en otras áreas que no son de tu dominio, vuelves a cumplir el rol de "novicio" y a sufrir cuando alguien "experto" no logra (o no quiere) bajar al nivel del que no sabe.

Es la diferencia de ser médico toda la vida pero nunca paciente ... pero cuando tienes que sufrir como médico lo que es estar del otro lado, creo que cambias para siempre la forma de ver las cosas.

Como decía Bruce Lee: "la mejor defensa es un buen ataque" ... ¿para qué vamos a estar dando vueltas si podemos ser directos y efectivos con un gancho al mentón? ;-)

Anónimo dijo...

Mis Felicitaciones Excelente Post

Enrique Place dijo...

Bueno, muchas gracias, espero haber tocado un tema complejo de forma simple y entendible. ;-)

Unknown dijo...

Hacen falta explicaciones de este tipo, no confundís ponerte al nivel del que no sabe, con bajar el nivel para el que no sabe.
Voy a leer un poco mas este sitio porque veo que podré absorver unas cuantas cosas.

saludos y seguí adelante

YESYK dijo...

podrias motrar el ejemplo en c# o en vbnet(visual no en consola)

Enrique Place dijo...

Que tal YESYK ;-)

La idea de los patrones es que son independientes del lenguaje de turno... no deberías tener problemas de aplicarlo usando los ejemplos que se muestran en el post.

Saludos! ;-)

Unknown dijo...

Excelente post...muy bueno...se ve que esto se usa en los cms (gestores de contenidos) pa el manejo de distintos themes(temas)..son implementaciones de clases que definen algoritmos de renderizacion de paginas...
muy bueno..bien explicado...excelente aporte gracias!!
Sandino
Montevideo-Uruguay

Enriqueta dijo...

si tenemos dos procesos 1 y 2 y ellos realizan los pasos a,b,c. Pero difieren en el último paso, el proceso 1 realiza además el paso h y el proceso 2 el paso j. Seria conveniente utilizar este patrón?, cómo se implementaria?

Deivid77 dijo...

He buscado mucho en internet sobre patrones de diseño y esta es la explicacion mas clara que he encontrado para una no experto como yo, muchar gracias enrique

Deivid77 dijo...

He buscado mucho en internet sobre patrones de diseño y esta es la explicacion mas clara que he encontrado para una no experto como yo, muchar gracias enrique

Entradas populares