Template-Method-Muster
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 99 von 104.
Das Template-Method-Muster definiert das Grundgerüst 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, bei dem ganze Algorithmen ausgetauscht werden, hält Template Method den Algorithmus fest und ermöglicht gleichzeitig 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:
// Schablonenmethode - 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";
}
};
int main() {
CSVProcessor csv;
csv.process(); // Verwendet standardmäßige saveResults
JSONProcessor json;
json.process(); // Verwendet benutzerdefinierte saveResults
}Die Methode process() ist die Template-Methode – sie definiert die feste Reihenfolge der Schritte. 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.
Verwende Template Method, wenn du einen Algorithmus mit unveränderlichen Schritten hast, aber Flexibilität bei bestimmten Operationen benötigst, oder wenn du Codeduplizierung in ähnlichen Klassen vermeiden möchtest.
Aufgabe
EinfachLass uns ein Berichtsgenerator-System mithilfe des Template-Method-Musters erstellen. Du wirst ein Framework entwickeln, in dem verschiedene Berichtstypen (wie Verkaufsberichte und Inventarberichte) demselben Erstellungsprozess folgen, aber jeder Berichtstyp bestimmte Schritte anpasst. Dies ist ein perfektes Szenario für die Template Method: Der allgemeine Ablauf bleibt festgelegt, während einzelne Schritte variieren.
Du wirst deinen Code auf drei Dateien verteilen:
ReportGenerator.h: Definiere die abstrakte Basisklasse mit der Template-Methode.Erstelle eine Klasse
ReportGenerator, die das Grundgerüst des Algorithmus zur Berichtserstellung definiert. Deine Template-MethodegenerateReport()sollte diese Schritte in der angegebenen Reihenfolge ausführen:gatherData(): rein virtuell, muss von Unterklassen implementiert werdenformatReport(): rein virtuell, muss von Unterklassen implementiert werdenaddHeader(): ein Hook mit Standardverhalten, der--- Report ---ausgibtprintReport(): rein virtuell, muss von Unterklassen implementiert werden
Die Methode
addHeader()dient als Hook, den Unterklassen optional überschreiben können, um die Kopfzeile anzupassen.Reports.h: Implementiere zwei konkrete Berichtsgeneratoren.Erstelle eine Klasse
SalesReport, die:gatherData()gibtGathering sales data from databaseausformatReport()gibtFormatting sales figuresausprintReport()gibtSales Total: $[amount]aus, wobei amount an den Konstruktor übergeben wird
Erstelle eine Klasse
InventoryReport, die:gatherData()gibtScanning inventory recordsausformatReport()gibtOrganizing inventory by categoryausaddHeader()überschreibt den Hook, um=== Inventory Report ===auszugebenprintReport()gibtItems in stock: [count]aus, wobei count an den Konstruktor übergeben wird
Beide Klassen sollten ihre jeweiligen Werte (amount oder count) über ihre Konstruktoren akzeptieren.
main.cpp: Veranschauliche das Template-Method-Muster.Lies zwei Eingaben ein:
- Verkaufsbetrag (Ganzzahl)
- Inventarbestand (Ganzzahl)
Erstelle einen
SalesReportmit dem Verkaufsbetrag und generiere ihn. Erstelle anschließend einenInventoryReportmit dem Inventarbestand und generiere ihn. Gib aus Gründen der Lesbarkeit eine Leerzeile zwischen den beiden Berichten aus.
Beispielsweise bei 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: 250Bei 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: 120Beachte, wie beide Berichte exakt derselben in generateReport() definierten Abfolge von Schritten folgen, diese Schritte jedoch jeweils unterschiedlich implementieren. Der InventoryReport zeigt außerdem, wie die Hook-Methode überschrieben wird, um die Kopfzeile anzupassen, während SalesReport das Standardverhalten verwendet. Dies ist das Template-Method-Muster in Aktion: Die Struktur des Algorithmus ist in der Basisklasse festgelegt, aber die Details bleiben flexibel.
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: Gib eine Leerzeile zwischen den Berichten aus
// TODO: Create an InventoryReport with inventoryCount and generate it
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator11Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixins mittels CRTPPimpl-IdiomTyplöschungEnum-Klassen & starke TypisierungAusnahmebehandlung in OOPBenutzerdefinierte Ausnahmehierarchien14Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterTemplate-Method-MusterZustandsmusterKompositmusterRAII als Muster3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler