2009-03-23 16 views
25

El patrón de método de plantilla y el patrón de estrategia hacen más o menos lo mismo. Entiendo las diferencias básicas entre ellos (el método de la plantilla se basa en la herencia, la estrategia se basa en la composición), pero ¿hay alguna guía decente sobre cuándo elegir una sobre la otra? Parece que básicamente hacen lo mismo.Cuándo usar el método de plantilla vs. ¿Estrategia?

Respuesta

18

Uso el método de Plantilla cuando el algoritmo necesita conocimiento de las partes internas de los objetos sobre los que se ejecuta.

En todos los demás casos (es decir, cuando el algoritmo solo necesita utilizar la interfaz del objeto), trato de usar Strategy.

Además, la estrategia solo es útil cuando hay algoritmos reales para implementar: si la única diferencia entre clases es (por ejemplo) qué valor simple devolver, utilice el método de plantilla.

2

Uno de los principios centrales de diseño de OO es "Favorecer la composición sobre la herencia", por lo que sugiere favorecer el patrón de estrategia. Obviamente, depende de lo que intenta lograr en un escenario particular.

31

La estrategia permite utilizar un algoritmo reutilizable en más de un lugar. Si tiene un algoritmo que puede ser proporcionado por su consumidor y puede usarse en varios lugares, este es un buen lugar para la estrategia (algoritmos de clasificación, predicados, comparadores ... son buenos ejemplos de eso).

El método de plantilla está específicamente dirigido a casos en los que desea que las personas hereden de su clase y quiere que puedan anular su implementación de forma controlada (básicamente evitando que reemplacen todas sus tuberías y ofreciéndoles un punto de extensión específico sin arriesgar un problema porque no llamaron al método base o lo llamaron en el momento equivocado).

Pueden ser similares, y pueden servir para el mismo tipo de propósito dependiendo de lo que realmente está haciendo. Al igual que con todos los patrones de diseño, es difícil responder a esa pregunta porque no hay realmente una respuesta definitiva. En realidad, es más fácil decidir en contexto ...

5

Puede crear un gran árbol de herencia solo para cambiar uno de los comportamientos de N. Y puedes crear el segundo gran árbol de herencia para cambiar el segundo comportamiento de N.

Pero también puedes descargar tu árbol creando pequeños árboles de estrategia.

Si notó que agrega más y más clases solo para agregar algunos cambios en algún comportamiento, es hora de proporcionar estrategias a sus clases.

13

Considere el uso de la estrategia cuando:

  • Su comportamiento objeto necesita ser cambiado en tiempo de ejecución.
  • Ya tiene jerarquía de clases por otros criterios.
  • Desea compartir la lógica de estrategia en diferentes clases.

En otros casos, debería ser suficiente para usar el patrón de la plantilla.

+0

es que 'cuando todos estos' o 'cuando cualquiera de estos'? – xtofl

+1

seguramente "cuando cualquiera de estos es cierto". –

17

De hecho, los dos se pueden usar de manera bastante efectiva.

No piense en patrones como recetas con un código específico para implementarlos.

El objetivo del diseño es la clave, y puede haber muchas implementaciones. Al mencionar un nombre de patrón en su código en alguna parte, está permitiendo que un lector conozca su intención cuando escribió ese código. La implementación es secundaria.

El método de la plantilla le proporciona un "algoritmo con pasos reemplazables". (El algoritmo normalmente se define en un método no invalidable (final o privado, por ejemplo))

La implementación de GoF de este concepto utiliza la herencia y la anulación de método para reemplazar esos pasos.

Sin embargo, usted todavía está utilizando el método de la plantilla si esas medidas son reemplazadas por las estrategias .

Por ejemplo, piense en una clase que quiera recorrer un árbol binario inorder y "hacer algo" en cada nodo.

La intención es que el método inorder() sea un método de plantilla: la estructura de la caminata es siempre la misma.

El método "gancho", la parte que "hace algo" puede implementarse como método en la misma clase (y anularse en subclases para cambiar el comportamiento), o externamente, en cuyo caso es una estrategia para "hacer algo" ".

+3

+1 para ** método de plantilla es para un algoritmo con pasos reemplazables ** – Fuhrmanator

+1

+1 para desacoplamiento de herencia y método de Plantilla, es decir, cuando se reemplazan algunos pasos en un algoritmo fijo incluso utilizando composición de objetos su patrón de método de plantilla sigue –

3

Me gustaría aceptar y en segundo lugar la explicación de Scott.

Plantilla patrón = preocupa por dibujar las líneas genéricos a lo largo de las que se realizará una operación en - plantillas - básicamente un "algoritmo con pasos reemplazables" (muy bien acuñado) donde los pasos reemplazables pueden delegarse utilizando la estrategia concepto de patrón.

Estrategia patrón = sólo se preocupa por desacoplar el cliente de la aplicación subrayando de una operación cuyo resultado tiene que cumplir siempre por unas reglas predeterminadas (como la clasificación, donde el resultado es siempre una lista ordenada, pero es posible que deffer de clasificación real para clasificar por burbujas o para ordenar rápidamente).

Saludos.

6

que no están de acuerdo con esta afirmación (de this answer):

método de la plantilla "está específicamente dirigido a los casos en los que desee que la gente sea capaz de heredar de su clase y quiere que sean capaces para anular su implementación de manera controlada ".


Si quieres que la gente heredan de su clase, entonces usted está queriendo una implementación específica, en lugar de querer un comportamiento particular. Eso me huele mal.

Una cosa válida que DESEAR es la capacidad de anular o proporcionar implementaciones de pasos individuales de un algoritmo. Ese objetivo puede lograrse mediante métodos de plantilla (donde podemos anular selectivamente los métodos protegidos) o el patrón de estrategia (donde implementamos implementaciones).

Si está creando una clase que implementa un algoritmo y desea permitir que otros desarrolladores modifiquen los pasos en ese algoritmo, ese es su requisito.Su única decisión es permitirles hacer eso a través de la herencia o la composición.

En igualdad de condiciones, debemos favorecer la composición por sobre la herencia, pero solo deberíamos llegar a la decisión de herencia/composición, primero determinando cuál es nuestro objetivo (puede que no necesitemos ninguno).

Nunca comenzaría con "Quiero permitir que hereden de esta clase". Eso es carrito antes del caballo IMO.

0

Casi siempre me decantaría por la razón importante de que el código del cliente no dependa de la implementación, mientras que en el patrón de la plantilla parte de la implementación permanece en la clase abstracta y cualquier cambio en la clase abstracta puede necesitar cambiar al cliente. a menudo da como resultado un código rígido y terminamos como desarrolladores diciendo que "esto resultó ser un cambio mayor de lo que esperaba".

Sin embargo, en casos en que es muy útil para obtener el código común en una clase abstracta, no dudaría en hacerlo y también tratar de mantener el código relacionado con código de cliente fuera de ella

0

Mi resumen: El patrón de estrategia está más vagamente acoplado que el patrón de Método de plantilla, que generalmente es algo bueno.

Robert C. Martin en TEMPLATE METHOD & STRATEGY: Inheritance vs. Delegation

Por lo tanto, el patrón de estrategia ofrece una ventaja adicional sobre el patrón MÉTODO PLANTILLA. Mientras que el patrón TEMPLATE METHOD permite que un algoritmo genérico manipule muchas posibles implementaciones detalladas de , al ajustarse totalmente al DIP, el patrón STRATEGY permite además que cada implementación detallada sea manipulada por muchos algoritmos genéricos diferentes.

DIP es la dependencia Inversion Principio:

A. módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deberían depender de abstracciones. B. Las abstracciones no deben depender de los detalles. Los detalles deben depender de las abstracciones.

(Wikipedia y Martin again).

-1

Preferiría usar una combinación de ambos, volcar la implementación predeterminada (desde el patrón de Plantilla) en la clase Contexto del patrón de estrategia. De esta forma, puedo aplicar el método de llamada del usuario al que quiero que llamen para que el orden de ejecución en los pasos del algoritmo permanezca controlado.

/** 
* enables replaceable steps in algorithm 
*/ 
public interface HouseStrategy{ 
     void buildWalls(); 
     void buildPillars(); 
} 


public class HouseContext{ 


    //public API that enforces order of execution 
    public void build(HouseStrategy strategy){ 
     buildFoundation();//default implementation 
     strategy.buildPillars();//delegated to concrete strategy 
     strategy.buildWalls();//delegated to concrete strategy 
     buildWindows();//default implementation 
    } 

    //default implementation 
    private void buildWindows() { 
     System.out.println("Building Glass Windows"); 
    } 
    //default implementation 
    private void buildFoundation() { 
     System.out.println("Building foundation with cement,iron rods and sand"); 
    } 

} 

public class WoodenHouse implements HouseStrategy { 

    @Override 
    public void buildWalls() { 
     System.out.println("Building Wooden Walls"); 
    } 

    @Override 
    public void buildPillars() { 
     System.out.println("Building Pillars with Wood coating"); 
    } 

} 

public class GlassHouse implements HouseStrategy { 

    @Override 
    public void buildWalls() { 
     System.out.println("Building Wooden Of glass"); 
    } 

    @Override 
    public void buildPillars() { 
     System.out.println("Building Pillars with glass coating"); 
    } 

} 

Como podemos ver, las estrategias concretas aún están abiertas a la extensión. Al igual que en,

public class GlassHouse implements HouseStrategy,EarthquakeResistantHouseStrategy{......} 

El uso

HouseContext context = new HouseContext(); 

    WoodenHouse woodenHouseStrategy = new WoodenHouse(); 
    context.build(woodenHouseStrategy); 

    GlassHouse glassHouseStrategy = new GlassHouse(); 
    context.build(glassHouseStrategy); 

Una desventaja que veo aquí es que las estrategias de hormigón sólo pueden cambiar el comportamiento variante del algoritmo es decir buildWalls() y buildPillars(). Si necesitamos cambiar las partes invariables, es decir, buildFoundation() y buildWindows(), necesitamos hacer que otra clase Context implemente el nuevo comportamiento. Aún así, obtenemos reusabilidad del código que no se encuentra en el patrón de estrategia pura :-)

Cuestiones relacionadas