Template method
Propósito
Define el esqueleto de un algoritmo en una operación, difiriendo algunos pasos a subclases. Plantilla
Método que permite a las subclases redefinir ciertos pasos de un algoritmo sin cambiar la estructura del algoritmo.
del algoritmo.
Explicación
Ejemplo del mundo real
Los pasos generales para robar un objeto son los mismos. Primero, eliges al objetivo, luego lo confundes...
y finalmente, robas el objeto. Sin embargo, hay muchas maneras de implementar estos
pasos.
En palabras sencillas
El patrón Template Method esboza los pasos generales en la clase padre y deja que las implementaciones hijas concretas definan los detalles.
Wikipedia dice
En programación orientada a objetos, el método de plantillas es uno de los patrones de diseño de comportamiento
identificados por Gamma et al. en el libro Design Patterns. El método de plantilla es un método en una
superclase, normalmente una superclase abstracta, y define el esqueleto de una operación en términos de
una serie de pasos de alto nivel. Estos pasos son a su vez implementados por métodos de ayuda adicionales
en la misma clase que el método de plantilla.
Ejemplo programático
Presentemos primero la clase de método de plantilla junto con sus implementaciones concretas.
Para asegurarse de que las subclases no sobrescriben el método de plantilla, el método de plantilla (en nuestro caso
método steal
) debe ser declarado final
, de lo contrario el esqueleto definido en la clase base podría
ser sobreescrito en subclases.
@Slf4j
public abstract class StealingMethod {
protected abstract String pickTarget();
protected abstract void confuseTarget(String target);
protected abstract void stealTheItem(String target);
public final void steal() {
var target = pickTarget();
LOGGER.info("The target has been chosen as {}.", target);
confuseTarget(target);
stealTheItem(target);
}
}
@Slf4j
public class SubtleMethod extends StealingMethod {
@Override
protected String pickTarget() {
return "shop keeper";
}
@Override
protected void confuseTarget(String target) {
LOGGER.info("Approach the {} with tears running and hug him!", target);
}
@Override
protected void stealTheItem(String target) {
LOGGER.info("While in close contact grab the {}'s wallet.", target);
}
}
@Slf4j
public class HitAndRunMethod extends StealingMethod {
@Override
protected String pickTarget() {
return "old goblin woman";
}
@Override
protected void confuseTarget(String target) {
LOGGER.info("Approach the {} from behind.", target);
}
@Override
protected void stealTheItem(String target) {
LOGGER.info("Grab the handbag and run away fast!");
}
}
Aquí está la clase ladrón halfling que contiene el método de plantilla.
public class HalflingThief {
private StealingMethod method;
public HalflingThief(StealingMethod method) {
this.method = method;
}
public void steal() {
method.steal();
}
public void changeMethod(StealingMethod method) {
this.method = method;
}
}
Y por último, mostramos cómo el ladrón halfling utiliza los diferentes métodos de robo.
var thief = new HalflingThief(new HitAndRunMethod());
thief.steal();
thief.changeMethod(new SubtleMethod());
thief.steal();
Diagrama de clases
Aplicabilidad
El patrón Template Method debería utilizarse
- Para implementar las partes invariantes de un algoritmo una vez y dejar que las subclases implementen el comportamiento que puede variar.
- Cuando el comportamiento común entre subclases debe ser factorizado y localizado en una clase común para evitar la duplicación de código. Este es un buen ejemplo de "refactorizar para generalizar", tal y como lo describen Opdyke y Johnson. Primero se identifican las diferencias en el código existente y luego se separan las diferencias en nuevas operaciones. Por último, se sustituye el código diferente por un método de plantilla que llama a una de estas nuevas operaciones
- Para controlar las extensiones de las subclases. Puede definir un método de plantilla que llame a operaciones "gancho" en puntos específicos, permitiendo así extensiones sólo en esos puntos
Tutoriales
Usos conocidos
- javax.servlet.GenericServlet.init:
El métodoGenericServlet.init(ServletConfig config)
llama al método sin parámetrosGenericServlet.init()
que está pensado para ser sobreescrito en subclases.
El métodoGenericServlet.init(ServletConfig config)
es el método plantilla en este ejemplo.