Menu
Coddy logo textTech

Template-Method-Muster

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 99 von 104.

Das Template Method Pattern definiert das Skelett eines Algorithmus in einer Basisklasse und ermöglicht es Unterklassen, bestimmte Schritte zu überschreiben, ohne die Gesamtstruktur des Algorithmus zu ändern. Im Gegensatz zu Strategy, das ganze Algorithmen austauscht, behält Template Method den Algorithmus bei, erlaubt aber die Anpassung einzelner Schritte.

Die Basisklasse implementiert die Template-Methode (den Algorithmus) und ruft abstrakte oder virtuelle Methoden auf, die Unterklassen bereitstellen müssen:

#include <iostream>

class DataProcessor {
public:
    // Template-Methode - definiert das Algorithmus-Skelett
    void process() {
        loadData();
        processData();
        saveResults();
    }
    
    virtual ~DataProcessor() = default;
    
protected:
    virtual void loadData() = 0;      // Muss implementiert werden
    virtual void processData() = 0;   // Muss implementiert werden
    
    // Hook - optionales Überschreiben mit Standardverhalten
    virtual void saveResults() {
        std::cout << "Saving to default location\n";
    }
};

class CSVProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading CSV file\n";
    }
    void processData() override {
        std::cout << "Parsing CSV data\n";
    }
};

class JSONProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading JSON file\n";
    }
    void processData() override {
        std::cout << "Parsing JSON data\n";
    }
    void saveResults() override {
        std::cout << "Saving to cloud storage\n";
    }
};

int main() {
    CSVProcessor csv;
    csv.process();  // Verwendet Standard-saveResults
    
    JSONProcessor json;
    json.process(); // Verwendet benutzerdefiniertes saveResults
}

Die Methode process() ist die Template-Methode – sie definiert die feste Abfolge von Schritten. Unterklassen implementieren loadData() und processData() unterschiedlich, aber die Reihenfolge ändert sich nie. Die Methode saveResults() ist ein „Hook“ – sie verfügt über eine Standardimplementierung, die Unterklassen optional überschreiben können.

Verwenden Sie die Template-Methode, wenn Sie einen Algorithmus mit unveränderlichen Schritten haben, aber Flexibilität bei bestimmten Operationen benötigen, oder wenn Sie Code-Duplizierung in ähnlichen Klassen vermeiden möchten.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Report Generator-System unter Verwendung des Template Method Patterns erstellen. Sie werden ein Framework entwickeln, in dem verschiedene Arten von Berichten (wie Verkaufsberichte und Inventarberichte) demselben Erstellungsprozess folgen, aber jeder Berichtstyp spezifische Schritte anpasst. Dies ist ein perfektes Szenario für die Template-Methode – der gesamte Workflow bleibt fest, während die einzelnen Schritte variieren.

Sie werden Ihren Code in drei Dateien organisieren:

  • ReportGenerator.h: Definieren Sie die abstrakte Basisklasse mit der Template-Methode.

    Erstellen Sie eine Klasse ReportGenerator, die das Skelett des Berichterstellungsalgorithmus definiert. Ihre Template-Methode generateReport() sollte diese Schritte in der folgenden Reihenfolge ausführen:

    1. gatherData() — rein virtuell, muss von Unterklassen implementiert werden
    2. formatReport() — rein virtuell, muss von Unterklassen implementiert werden
    3. addHeader() — ein Hook mit Standardverhalten, der --- Report --- ausgibt
    4. printReport() — rein virtuell, muss von Unterklassen implementiert werden

    Die Methode addHeader() dient als Hook, den Unterklassen optional überschreiben können, um den Header anzupassen.

  • Reports.h: Implementieren Sie zwei konkrete Berichtsgeneratoren.

    Erstellen Sie eine Klasse SalesReport, die:

    • gatherData() gibt Gathering sales data from database aus
    • formatReport() gibt Formatting sales figures aus
    • printReport() gibt Sales Total: $[amount] aus, wobei amount an den Konstruktor übergeben wird

    Erstellen Sie eine Klasse InventoryReport, die:

    • gatherData() gibt Scanning inventory records aus
    • formatReport() gibt Organizing inventory by category aus
    • addHeader() überschreibt den Hook, um === Inventory Report === auszugeben
    • printReport() gibt Items in stock: [count] aus, wobei count an den Konstruktor übergeben wird

    Beide Klassen sollten ihre jeweiligen Werte (amount oder count) über ihre Konstruktoren entgegennehmen.

  • main.cpp: Demonstrieren Sie das Template Method Pattern.

    Lesen Sie zwei Eingaben ein:

    1. Verkaufsbetrag (Integer)
    2. Inventarbestand (Integer)

    Erstellen Sie einen SalesReport mit dem Verkaufsbetrag und generieren Sie ihn. Erstellen Sie dann einen InventoryReport mit dem Inventarbestand und generieren Sie ihn. Geben Sie zur besseren Lesbarkeit eine Leerzeile zwischen den beiden Berichten aus.

Zum Beispiel mit den Eingaben 15000 und 250:

Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $15000

Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 250

Mit den Eingaben 8500 und 120:

Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $8500

Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 120

Beachten Sie, wie beide Berichte genau der gleichen Abfolge von Schritten folgen, die in generateReport() definiert ist, aber jeder diese Schritte unterschiedlich implementiert. Der InventoryReport demonstriert zudem das Überschreiben der Hook-Methode zur Anpassung des Headers, während SalesReport den Standard verwendet. Dies ist das Template Method Pattern in Aktion – die Algorithmusstruktur ist in der Basisklasse festgelegt, aber die Details sind flexibel.

Spickzettel

Das Template Method Pattern (Schablonenmethode) definiert das Skelett eines Algorithmus in einer Basisklasse und ermöglicht es Unterklassen, bestimmte Schritte zu überschreiben, ohne die Gesamtstruktur des Algorithmus zu ändern.

Die Basisklasse implementiert die Template-Methode (den Algorithmus) und ruft abstrakte oder virtuelle Methoden auf, die von den Unterklassen bereitgestellt werden müssen:

#include <iostream>

class DataProcessor {
public:
    // Template-Methode - definiert das Skelett des Algorithmus
    void process() {
        loadData();
        processData();
        saveResults();
    }
    
    virtual ~DataProcessor() = default;
    
protected:
    virtual void loadData() = 0;      // Muss implementiert werden
    virtual void processData() = 0;   // Muss implementiert werden
    
    // Hook - optionales Überschreiben mit Standardverhalten
    virtual void saveResults() {
        std::cout << "Saving to default location\n";
    }
};

class CSVProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading CSV file\n";
    }
    void processData() override {
        std::cout << "Parsing CSV data\n";
    }
};

class JSONProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "Loading JSON file\n";
    }
    void processData() override {
        std::cout << "Parsing JSON data\n";
    }
    void saveResults() override {
        std::cout << "Saving to cloud storage\n";
    }
};

Die Template-Methode definiert die feste Abfolge von Schritten. Unterklassen implementieren abstrakte Methoden unterschiedlich, aber die Reihenfolge ändert sich nie. Hook-Methoden verfügen über Standardimplementierungen, die Unterklassen optional überschreiben können.

Verwenden Sie das Template Method Pattern, wenn Sie einen Algorithmus mit invarianten Schritten haben, aber Flexibilität bei bestimmten Operationen benötigen, oder wenn Sie Codeduplizierung über ähnliche Klassen hinweg vermeiden möchten.

Probier es selbst

#include <iostream>
#include "Reports.h"

using namespace std;

int main() {
    // Eingaben lesen
    int salesAmount;
    int inventoryCount;
    cin >> salesAmount;
    cin >> inventoryCount;
    
    // TODO: Erstelle einen SalesReport mit salesAmount und generiere ihn
    
    // TODO: Drucke eine Leerzeile zwischen den Berichten
    
    // TODO: Erstelle einen InventoryReport mit inventoryCount und generiere ihn
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung