Template Method Pattern

Template Method Pattern

vg

Das Template Method Pattern ist ein Verhaltensmuster, das eine Vorlage für Algorithmen bereitstellt. Es definiert die Struktur eines Algorithmus, lässt jedoch bestimmte Schritte von den Unterklassen implementieren. Dadurch wird das Grundgerüst des Algorithmus in der Basisklasse festgelegt, während die Variation der Schritte in den abgeleiteten Klassen erfolgt.

Was ist das Template Method Pattern?

Das Template Method Pattern sorgt dafür, dass der grundlegende Ablauf eines Algorithmus nicht verändert wird. Stattdessen können abgeleitete Klassen bestimmte Schritte des Algorithmus überschreiben. Es besteht aus einer abstrakten Methode (der Template-Methode), die den allgemeinen Ablauf des Algorithmus definiert, und einer Reihe von abstrakten Methoden, die in den Unterklassen implementiert werden.

Komponenten des Template Method Patterns

  1. AbstractClass: Diese Klasse enthält die Template-Methode, die den Ablauf des Algorithmus festlegt. Sie kann auch einige gemeinsame Implementierungen enthalten.
  2. ConcreteClass: Die konkreten Klassen überschreiben die abstrakten Methoden, um die spezifische Implementierung der Schritte bereitzustellen.
  3. TemplateMethod: Diese Methode ist in der abstrakten Klasse definiert und ruft die abstrakten Methoden in einer bestimmten Reihenfolge auf. Sie ist der „Rahmen“ des Algorithmus.

Beispiel des Template Method Patterns in C++

Im folgenden Beispiel wird das Template Method Pattern verwendet, um den Ablauf einer einfachen Zubereitung von Getränken zu modellieren. Die Zubereitung jedes Getränks folgt einem ähnlichen Prozess, aber einige Schritte variieren.

#include <iostream>

// AbstractClass: Die abstrakte Klasse, die die Template-Methode enthält
class Drink {
public:
    // Template-Methode
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // Abstrakte Methoden, die von den konkreten Klassen implementiert werden
    virtual void brew() = 0;
    virtual void addCondiments() = 0;

    // Gemeinsame Methoden, die in der Basis-Klasse definiert werden
    void boilWater() {
        std::cout << "Wasser wird gekocht." << std::endl;
    }

    void pourInCup() {
        std::cout << "Getränk wird in Tasse gegossen." << std::endl;
    }
};

// ConcreteClass: Eine konkrete Implementierung für Kaffee
class Coffee : public Drink {
public:
    void brew() override {
        std::cout << "Kaffee wird gebrüht." << std::endl;
    }

    void addCondiments() override {
        std::cout << "Kaffee wird mit Zucker und Milch verfeinert." << std::endl;
    }
};

// ConcreteClass: Eine konkrete Implementierung für Tee
class Tea : public Drink {
public:
    void brew() override {
        std::cout << "Tee wird gezogen." << std::endl;
    }

    void addCondiments() override {
        std::cout << "Tee wird mit Zitrone verfeinert." << std::endl;
    }
};

// Client-Code
int main() {
    Coffee coffee;
    Tea tea;

    std::cout << "Zubereitung Kaffee:" << std::endl;
    coffee.prepareRecipe();

    std::cout << "\nZubereitung Tee:" << std::endl;
    tea.prepareRecipe();

    return 0;
}

Erklärung des C++-Beispiels

  1. Drink (AbstractClass): Diese abstrakte Klasse stellt die Template-Methode prepareRecipe() bereit. Sie enthält auch die gemeinsamen Methoden boilWater() und pourInCup(), die unverändert bleiben. Die beiden abstrakten Methoden brew() und addCondiments() müssen von den konkreten Klassen überschrieben werden.
  2. Coffee und Tea (ConcreteClass): Diese beiden konkreten Klassen implementieren die abstrakten Methoden, um das spezifische Verhalten für Kaffee und Tee bereitzustellen. In Coffee wird der Kaffee gebrüht, während in Tea der Tee gezogen wird. Beide Klassen fügen ihre eigenen Zutaten hinzu.
  3. Client: Der Client ruft die Methode prepareRecipe() auf, um den gesamten Zubereitungsprozess zu starten. Die Template-Methode sorgt dafür, dass der Ablauf gleich bleibt, aber die spezifischen Schritte (wie brew und addCondiments) unterschiedlich sind.

Beispiel des Template Method Patterns in Python

Das Template Method Pattern ist ein Verhaltensmuster, bei dem eine abstrakte Klasse die Struktur eines Algorithmus definiert, aber bestimmte Schritte des Algorithmus an Unterklassen delegiert. Unterklassen implementieren dann diese abstrakten Schritte. Auf diese Weise wird der allgemeine Ablauf eines Prozesses in einer Basisklasse definiert, während spezifische Details von Unterklassen implementiert werden.

Hier ist ein einfaches Beispiel des Template Method Patterns in Python:

from abc import ABC, abstractmethod

# Die abstrakte Klasse, die das Template Method Pattern implementiert
class GetränkeZubereiter(ABC):

    # Die Template-Methode, die den allgemeinen Ablauf definiert
    def zubereiten(self):
        self.wasserErhitzen()
        self.zutatHinzufuegen()
        self.umruehren()
        self.servieren()

    # Konkrete Methode: Allgemeiner Ablauf für das Erhitzen von Wasser
    def wasserErhitzen(self):
        print("Wasser wird erhitzt.")

    # Abstrakte Methode: Muss von Unterklassen implementiert werden
    @abstractmethod
    def zutatHinzufuegen(self):
        pass

    # Konkrete Methode: Allgemeiner Ablauf zum Umrühren
    def umruehren(self):
        print("Getränk wird umgerührt.")

    # Konkrete Methode: Allgemeiner Ablauf zum Servieren
    def servieren(self):
        print("Getränk wird serviert.")

# Eine Unterklasse, die den spezifischen Schritt zum Hinzufügen der Zutat implementiert
class KaffeeZubereiter(GetränkeZubereiter):
    def zutatHinzufuegen(self):
        print("Kaffeepulver wird hinzugefügt.")

# Eine andere Unterklasse, die den spezifischen Schritt zum Hinzufügen der Zutat implementiert
class TeeZubereiter(GetränkeZubereiter):
    def zutatHinzufuegen(self):
        print("Teebeutel wird hinzugefügt.")

# Anwendung des Template Method Patterns
def main():
    kaffeekochen = KaffeeZubereiter()
    teeKochen = TeeZubereiter()

    print("Kaffee Zubereitung:")
    kaffeekochen.zubereiten()

    print("\nTee Zubereitung:")
    teeKochen.zubereiten()

if __name__ == "__main__":
    main()

Erklärung:

  1. GetränkeZubereiter: Diese Klasse definiert den allgemeinen Ablauf (Template) für das Zubereiten eines Getränks. Sie enthält sowohl konkrete Methoden (wasserErhitzen, umruehren, servieren) als auch eine abstrakte Methode (zutatHinzufuegen), die von den Unterklassen implementiert werden muss.
  2. KaffeeZubereiter und TeeZubereiter: Diese Unterklassen implementieren die abstrakte Methode zutatHinzufuegen, wobei die Zutat für den jeweiligen Getränketyps hinzugefügt wird.
  3. Template-Methode: Die Methode zubereiten in der Klasse GetränkeZubereiter ist die Template-Methode, die den allgemeinen Ablauf des Zubereitens eines Getränks vorgibt. Die spezifische Zutat wird jedoch durch die Unterklassen festgelegt.

Ausgabe:

Kaffee Zubereitung:
Wasser wird erhitzt.
Kaffeepulver wird hinzugefügt.
Getränk wird umgerührt.
Getränk wird serviert.

Tee Zubereitung:
Wasser wird erhitzt.
Teebeutel wird hinzugefügt.
Getränk wird umgerührt.
Getränk wird serviert.

Das Template Method Pattern hilft hier dabei, einen gemeinsamen Ablauf zu definieren, aber gleichzeitig die Flexibilität zu bewahren, dass jede Unterklasse ihre eigenen spezifischen Schritte zur Zubereitung eines Getränks definieren kann.

Vorteile des Template Method Patterns

  1. Wiederverwendbarkeit: Das Grundgerüst des Algorithmus wird in der Basisklasse definiert. Dadurch können die Unterklassen nur die variablen Schritte überschreiben, ohne den gesamten Algorithmus neu zu schreiben.
  2. Vermeidung von Duplikaten: Gemeinsame Schritte werden in der Basisklasse implementiert. So wird der Code in den konkreten Klassen minimiert.
  3. Flexibilität: Das Template Method Pattern erlaubt es, den allgemeinen Ablauf des Algorithmus zu verändern, ohne die Struktur zu beeinflussen. Nur die spezifischen Schritte müssen angepasst werden.
  4. Einheitlichkeit: Das Muster sorgt für Konsistenz, da der Ablauf des Algorithmus für alle konkreten Klassen gleich bleibt. Nur die Implementierung der Schritte unterscheidet sich.

Nachteile des Template Method Patterns

  1. Schwierigkeit bei Änderungen: Wenn sich der allgemeine Ablauf des Algorithmus ändert, muss die Basisklasse angepasst werden. Dies kann Auswirkungen auf alle abgeleiteten Klassen haben.
  2. Vererbungshierarchie: Das Pattern erfordert eine Vererbungshierarchie. Dies kann den Code komplexer und weniger flexibel machen, wenn viele verschiedene Verhaltensweisen erforderlich sind.
  3. Schwierigkeit bei sehr komplexen Algorithmen: Wenn der Algorithmus viele variierbare Teile hat, kann das Muster zu einer zu großen Anzahl von Methoden führen. Dies kann die Lesbarkeit und Wartbarkeit des Codes erschweren.

Wann sollte das Template Method Pattern eingesetzt werden und wann nicht?

Das Template Method Pattern sollte in folgenden Situationen eingesetzt werden:

1. Wiederverwendbare Algorithmen mit variablen Details

  • Wenn ein Algorithmus im Wesentlichen immer gleich bleibt, aber einige spezifische Schritte variieren können, ist das Template Method Pattern ideal. Die allgemeine Struktur wird in der Basisklasse definiert, und die Unterklassen implementieren die variablen Teile.
  • Beispiel: Ein Workflow für das Zubereiten von Getränken, bei dem der Ablauf immer gleich ist (Wasser erhitzen, umrühren, servieren), aber die Zutaten variieren (Kaffee, Tee, etc.).

2. Vermeidung von Code-Duplikation

  • Wenn du einen komplexen Algorithmus hast, der in mehreren Klassen verwendet wird, und viele Schritte des Algorithmus wiederverwendet werden, kannst du das Template Method Pattern nutzen, um den gemeinsamen Code in einer Basisklasse zu definieren. So vermeidest du Duplikation in den Unterklassen.
  • Beispiel: Verschiedene Arten von Dokumenten müssen gerendert werden (HTML, PDF, etc.), aber der Renderprozess folgt immer der gleichen Struktur.

3. Verhinderung von „Verletzung der Reihenfolge von Methodenaufrufen“

  • Manchmal ist es notwendig, die Reihenfolge von Methodenaufrufen zu kontrollieren. Das Template Method Pattern gibt die Reihenfolge vor, sodass die Unterklassen nicht die Reihenfolge der Schritte ändern können.
  • Beispiel: In einem Spiel könnte der allgemeine Ablauf der Spielführung immer gleich sein (Spiel starten, Level laden, Spieleraktionen ausführen, Ergebnis anzeigen), aber die Details variieren je nach Spieltyp. Die Reihenfolge wird durch die Template-Methode gesteuert.

4. Abstraktion und Flexibilität

  • Das Pattern bietet eine klare Trennung zwischen dem allgemeinen Ablauf und den spezifischen Details. Wenn du den allgemeinen Ablauf ändern möchtest (z. B. ein neuer Schritt im Algorithmus eingeführt wird), kannst du dies in der Basisklasse tun, ohne die Unterklassen zu verändern.
  • Beispiel: Eine allgemeine Validierungsmethode, bei der die Schritte der Validierung in der Basisimplementierung festgelegt sind, aber bestimmte Validierungslogik durch Unterklassen spezifiziert wird.

5. Verwendung von Hook-Methoden für Erweiterbarkeit

  • Wenn du zusätzliche Variabilität in einem Algorithmus haben möchtest, ohne die Template-Methode zu verändern, kannst du „Hook-Methoden“ einführen. Das sind Methoden, die in der Basisklasse implementiert werden, aber optional von den Unterklassen überschrieben werden können, um weiteres Verhalten zu spezifizieren.
  • Beispiel: Ein Reporting-System könnte eine allgemeine Struktur für das Erstellen eines Berichts vorgeben, aber Hooks wie beforeRendering oder afterRendering ermöglichen es den Unterklassen, bestimmte Schritte nach Bedarf hinzuzufügen.

6. Vermeidung von Fehlern durch feste Struktur

  • Das Template Method Pattern gibt eine feste Struktur vor, sodass Unterklassen nur die erforderlichen Schritte implementieren und keine kritischen Schritte vergessen oder durcheinander bringen können. Dies stellt sicher, dass der Algorithmus korrekt ausgeführt wird.
  • Beispiel: In einem Bestellprozess ist der Ablauf klar vorgegeben (Bestellung aufnehmen, Zahlungsabwicklung durchführen, Lieferung organisieren), aber die Details der Zahlungsabwicklung oder Lieferung variieren.

Wann sollte das Template Method Pattern nicht verwendet werden?

  1. Wenn der Algorithmus zu stark variiert: Wenn der Algorithmus oder die Schritte stark von Fall zu Fall unterschiedlich sind, kann das Template Method Pattern möglicherweise nicht die beste Lösung sein, da der Aufwand, die Basisklasse anzupassen, zu hoch wäre.
  2. Wenn es keine klar definierte Struktur gibt: Wenn der Ablauf eines Prozesses nicht vorab definiert oder schwer vorherzusagen ist, kann das Pattern weniger sinnvoll sein, da die Variabilität zu stark ist, um durch eine Template-Methode kontrolliert zu werden.
  3. Komplexität durch zu viele Unterklassen: Wenn es viele verschiedene Implementierungen für die variablen Teile gibt, kann das Pattern dazu führen, dass zu viele Unterklassen erforderlich sind, was den Code unnötig kompliziert und schwer wartbar macht.

Zusammenfassung:

Das Template Method Pattern ist besonders dann nützlich, wenn du einen allgemeinen Ablauf (Algorithmus) definieren möchtest, der in mehreren Klassen verwendet wird, wobei nur bestimmte Schritte variiert werden müssen. Es hilft, Code zu zentralisieren, die Reihenfolge von Operationen sicherzustellen und eine flexible Erweiterung der Schritte zu ermöglichen, ohne die Struktur zu verändern.

Fazit

Das Template Method Pattern ist ein nützliches Designmuster, um den Ablauf eines Algorithmus festzulegen, während bestimmte Schritte von Unterklassen implementiert werden können. Es hilft, Duplikate zu vermeiden und fördert die Wiederverwendbarkeit von Code. In unserem Beispiel zur Zubereitung von Getränken wird der allgemeine Ablauf in der abstrakten Klasse Drink definiert, während die konkreten Klassen wie Coffee und Tea nur die variablen Teile überschreiben. Dieses Muster eignet sich besonders, wenn der allgemeine Ablauf eines Prozesses konstant bleibt, aber einzelne Schritte anpassbar sein müssen.

Zur Pattern-Übersicht: Liste der Design-Pattern

com

Newsletter Anmeldung

Bleiben Sie informiert! Wir informieren Sie über alle neuen Beiträge (max. 1 Mail pro Woche – versprochen)