2009-03-23 8 views
25

Das Template-Methodenmuster und das Strategie-Pattern machen ungefähr die gleiche Sache. Ich verstehe die grundlegenden Unterschiede zwischen ihnen (Template-Methode ist vererbungsbasiert, Strategie ist kompositionsbasiert), aber gibt es irgendwelche anständigen Richtlinien, wann man sich für eines entscheidet? Es scheint, als würden sie im Grunde dasselbe tun.Wann die Vorlagenmethode Vs. Strategie?

Antwort

18

Ich benutze Template-Methode, wenn der Algorithmus Kenntnis der Interna der Objekte benötigt, auf denen er läuft.

In allen anderen Fällen (d. H. Wenn der Algorithmus nur die Schnittstelle des Objekts verwenden muss), versuche ich Strategie zu verwenden.

Weiter ist Strategie nur nützlich, wenn es Algorithmen gibt, die implementiert werden müssen: Wenn der einzige Unterschied zwischen Klassen ist (zum Beispiel) welcher einfache Wert zurückgegeben wird, verwenden Sie die Template-Methode.

2

Eines der zentralen OO-Design-Prinzipien ist "Favor Composition over Vererbung", so dass vorgeschlagen wird, das Strategie-Muster zu bevorzugen. Es hängt natürlich davon ab, was Sie in einem bestimmten Szenario erreichen möchten.

31

Strategie ermöglicht die Verwendung eines wiederverwendbaren Algorithmus an mehreren Stellen. Wenn Sie einen Algorithmus haben, der von Ihrem Kunden bereitgestellt werden kann und an mehreren Orten verwendet werden kann, ist dies ein guter Ort für die Strategie (Sortieralgorithmen, Prädikate, Vergleiche ... sind gute Beispiele dafür).

Die Template-Methode ist speziell auf Fälle ausgerichtet, in denen Personen in der Lage sein sollen, von Ihrer Klasse zu erben und sie in der Lage zu sein, Ihre Implementierung kontrolliert zu überschreiben (was im Grunde verhindert, dass alle Ihre Installationen ersetzt werden) spezifischen Erweiterungspunkt, ohne ein Problem zu riskieren, da sie die Basismethode nicht aufgerufen oder zur falschen Zeit aufgerufen haben).

Sie können ähnlich sein, und sie können die gleiche Art von Zweck je nachdem, was Sie tatsächlich tun, dienen. Wie bei allen Entwurfsmustern ist es schwierig, eine solche Frage zu beantworten, weil es nicht wirklich eine definitive Antwort gibt. Es ist einfacher, sich im Kontext zu entscheiden ...

5

Sie können einen großen Vererbungsbaum erstellen, um nur eines der N-Verhaltensweisen zu ändern. Und Sie können einen zweiten großen Vererbungsbaum erstellen, um das zweite des N-Verhaltens zu ändern.

Aber auch Sie können Ihren Baum entladen, indem Sie kleine Strategiebäume erstellen.

Wenn Sie also festgestellt haben, dass Sie mehr und mehr Klassen hinzufügen, nur um einige Änderungen in einem Verhalten hinzuzufügen - ist es an der Zeit, Ihre Klassen mit Strategien zu versorgen.

13

Betrachten Nutzungsstrategie bei:

  • Ihr Objektverhalten in Runtime geändert werden muss.
  • Sie haben bereits eine Klassenhierarchie nach anderen Kriterien.
  • Sie möchten die Strategielogik für verschiedene Klassen freigeben.

In anderen Fällen sollte es ausreichen, Vorlagenmuster zu verwenden.

+0

ist das "wenn alle diese" oder "wenn einer von diesen"? – xtofl

+1

sicherlich "wenn einer von diesen ist wahr." –

17

Die beiden können tatsächlich sehr effektiv zusammen verwendet werden.

Denken Sie nicht an Muster als Rezepte mit spezifischem Code, um sie zu implementieren.

Es ist die Konstruktionsabsicht, die der Schlüssel ist, und es kann viele Implementierungen geben. Indem Sie irgendwo in Ihrem Code einen Musternamen erwähnen, lassen Sie einen Leser Ihre Absicht wissen, als Sie diesen Code geschrieben haben. Die Implementierung ist sekundär.

Template-Methode gibt Ihnen einen "Algorithmus mit austauschbaren Schritten". (Der Algorithmus wird normalerweise in einer nicht überschreibbaren Methode definiert (final oder privat zum Beispiel))

Die GoF-Implementierung dieses Konzepts verwendet Vererbung und Methodenüberschreibung, um diese Schritte zu ersetzen.

jedoch, Sie verwenden noch Template-Methode, wenn diese Schritte durch Strategien ersetzt werden.

Denken Sie zum Beispiel an eine Klasse, die einen binären Baum in Ordnung gehen und an jedem Knoten "etwas tun" möchte.

Die Absicht ist, dass die inorder() Methode ist eine Vorlage Methode - die Struktur der Wanderung ist immer gleich. Die "Hook" -Methode, der Teil, der "etwas macht" kann als eine Methode in derselben Klasse implementiert werden (und in Unterklassen überschrieben werden, um das Verhalten zu ändern), oder extern, in diesem Fall ist es eine Strategie für "etwas zu tun" ".

+3

+1 für ** Template-Methode ist für einen Algorithmus mit ersetzbaren Schritten ** – Fuhrmanator

+1

+1 für entkoppelnde Vererbung und Template-Methode, d. H. Wenn einige Schritte in einem festen Algorithmus sogar durch Verwendung der Objektkomposition seine noch Template-Methode Muster ersetzt werden –

3

Ich würde gerne zustimmen und zweiten Scott Erklärung.

Template pattern = kümmert sich um die generischen Zeichnen von Linien, entlang derer eine Operation wird auf getragen werden - Templat - im Grunde ein „Algorithmus mit austauschbaren Schritten“ (sehr gut geprägt), wobei die austauschbaren Schritte mit der Strategie delegiert werden kann Musterkonzept.

Strategie pattern = kümmert sich nur um den Client vom unterstreicht Implementierung einer Operation, deren Ausgang Entkopplung muss immer von einigen vorgegebenen Regeln halten (wie Sortieren, wenn das Ergebnis immer eine sortierte Liste ist, aber Sie können de eigentliche Sortier Deffer blubbern oder schnell sortieren).

Prost.

6

Ich bin nicht einverstanden mit dieser Aussage (von this answer):

„Template-Methode speziell auf Fälle ausgerichtet ist, wo Sie wollen Menschen in der Lage sein, von Ihrer Klasse zu erben und wollen sie in der Lage seine außer Kraft zu setzen Ihre Implementierung in einer kontrollierten Art und Weise. "


Wenn Sie wollen die Menschen aus Ihrer Klasse erben dann sind Sie eine spezielle Implementierung WANTING, anstatt ein bestimmtes Verhalten zu wollen. Das riecht schlecht für mich.

Eine gültige Sache zu WANT ist die Fähigkeit, Implementierungen einzelner Schritte eines Algorithmus zu überschreiben oder bereitzustellen. Dieses Ziel kann sowohl durch Template-Methoden (wo wir geschützte Methoden selektiv überschreiben können) als auch durch das Strategie-Pattern (wo wir Implementierungen injizieren) erreicht werden.

Wenn Sie eine Klasse erstellen, die einen Algorithmus implementiert, und Sie möchten, dass Schritte in diesem Algorithmus von anderen Entwicklern geändert werden, ist dies Ihre Anforderung.Ihre einzige Entscheidung ist, ob sie dies über Vererbung oder Zusammensetzung tun dürfen.

Wenn alle anderen Dinge gleich sind, sollten wir die Komposition über die Vererbung bevorzugen, aber wir sollten erst zur Erbschafts-/Zusammensetzungsentscheidung kommen, indem wir zuerst herausfinden, was unser Ziel ist (wir brauchen vielleicht keines).

Ich würde nie mit "Ich möchte ihnen erlauben, von dieser Klasse zu erben" starten. Das ist Wagen vor dem Pferd IMO.

0

Ich würde fast immer für Strategie aus dem sehr wichtigen Grund gehen, dass Client-Code keine Abhängigkeit von der Implementierung hat, während im Template-Muster Teil der Implementierung in der abstrakten Klasse bleibt und jede Änderung der abstrakten Klasse muss den Client sehr ändern oft führen sie zu starrem Code und am Ende sagen Entwickler, dass "dies eine größere Veränderung war, als ich erwartet hatte".

Aber in Fällen, wenn es wirklich hilfreich gemeinsamen Code in einer abstrakten Klasse zu erhalten zögern würde ich es nicht zu tun und versuchen, auch von ihm

0

Meine Zusammenfassung zu Client-Code weg bezogenen Code zu halten: Das Strategy-Muster ist lockerer gekoppelt als das Muster der Template-Methode, was im Allgemeinen eine gute Sache ist.

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

Somit stellt die Strategie-Muster einen zusätzlichen Vorteil gegenüber dem Template-Methode Muster. Während das TEMPLATE METHOD-Muster einen generischen Algorithmus erlaubt, viele mögliche detaillierte Implementierungen zu manipulieren, ermöglicht das STRATEGY-Muster durch vollständige Übereinstimmung mit dem DIP zusätzlich, dass jede detaillierte Implementierung durch viele verschiedene generische Algorithmen manipuliert werden kann.

DIP ist die Abhängigkeit Inversion Prinzip:

A. Hochrangigen Module sollten nicht auf Low-Level-Module ab. Beide sollten von Abstraktionen abhängen. B. Abstraktionen sollten nicht von Details abhängen. Details sollten von Abstraktionen abhängen.

(Wikipedia und Martin again).

-1

Ich würde bevorzugen, eine Mischung aus beiden zu verwenden, Dumping-Standardimplementierung (aus Vorlagenmuster) in Kontextklasse des Strategie-Patterns. Auf diese Weise kann ich den Benutzer zwingen, die Methode aufzurufen, die er aufrufen soll, damit die Reihenfolge der Ausführung auf den Schritten des Algorithmus kontrolliert bleibt.

/** 
* 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"); 
    } 

} 

Wie wir sehen können, sind konkrete Strategien noch offen für die Erweiterung. Wie in,

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

Die Verwendung

HouseContext context = new HouseContext(); 

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

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

Ein Nachteil, den ich hier sehe ist, dass konkrete Strategien nur die Variante Verhalten des Algorithmus heißt buildWalls ändern können() und buildPillars(). Wenn wir invariante Teile, d. H. BuildFoundation() und buildWindows(), ändern müssen, müssen wir eine andere Context-Klasse erstellen, die das neue Verhalten implementiert. Immer noch erhalten wir einige Code-Wiederverwendbarkeit, die nicht in reinem Strategie-Muster gefunden wird :-)

Verwandte Themen