Template-Method-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 97 von 110.
Das Template-Method-Muster definiert das Skelett eines Algorithmus in einer Basisklasse und ermöglicht es Unterklassen, bestimmte Schritte zu überschreiben, ohne die Gesamtstruktur zu ändern. Die Basisklasse steuert den Ablauf, während Unterklassen einzelne Schritte anpassen.
Dieses Muster verwendet Vererbung: Eine abstrakte Klasse definiert eine Template-Methode, die eine Abfolge von Schritten aufruft, von denen einige abstrakt sind (von Unterklassen implementiert werden müssen) und einige Standardimplementierungen haben:
abstract class DataProcessor {
// Template-Methode - definiert die Algorithmusstruktur
void process() {
readData();
processData();
saveData();
}
void readData(); // Abstrakt - Unterklassen müssen dies implementieren
void processData(); // Abstrakt - Unterklassen müssen dies implementieren
void saveData() { // Standardimplementierung
print('Data saved to database');
}
}
class CsvProcessor extends DataProcessor {
@override
void readData() => print('Reading CSV file');
@override
void processData() => print('Parsing CSV rows');
}
class JsonProcessor extends DataProcessor {
@override
void readData() => print('Reading JSON file');
@override
void processData() => print('Parsing JSON objects');
@override
void saveData() => print('Saved to cloud storage');
}
void main() {
var csv = CsvProcessor();
csv.process();
// Lesen der CSV-Datei
// Parsen der CSV-Zeilen
// Daten in der Datenbank gespeichert
var json = JsonProcessor();
json.process();
// Lesen der JSON-Datei
// Parsen der JSON-Objekte
// Im Cloud-Speicher gespeichert
}Die process()-Methode ist das Template – sie definiert die Reihenfolge der Operationen. Unterklassen implementieren readData() und processData() unterschiedlich und können optional saveData() überschreiben. Der entscheidende Vorteil ist, dass die Struktur des Algorithmus über alle Implementierungen hinweg konsistent bleibt, während Flexibilität in den einzelnen Schritten ermöglicht wird.
Aufgabe
EinfachLassen Sie uns ein Berichtserstellungssystem mit dem Template Method Pattern erstellen! Sie werden ein Framework entwickeln, in dem verschiedene Arten von Berichten derselben Gesamtstruktur folgen, aber jeder Berichtstyp spezifische Schritte wie die Datenerfassung und die Formatierung der Ausgabe anpasst.
Sie werden Ihren Code in zwei Dateien organisieren:
report_generator.dart: Diese Datei enthält Ihre abstrakte Basisklasse und die konkreten Berichtsimplementierungen. Erstellen Sie eine abstrakte KlasseReportGeneratormit einer Template-Methode namensgenerate(), die den Workflow der Berichtserstellung in dieser Reihenfolge definiert:fetchData(),analyzeData(),formatReport()unddeliver(). Die MethodenfetchData()undformatReport()sollten abstrakt sein (jeder Berichtstyp handhabt diese unterschiedlich), währendanalyzeData()standardmäßigAnalyzing data...unddeliver()standardmäßigReport delivered via email.ausgeben sollte. Erstellen Sie dann zwei konkrete Berichtsklassen:SalesReport- dessenfetchData()gibtFetching sales data from database...aus und dessenformatReport()gibtFormatting as bar charts and tables.aus.InventoryReport- dessenfetchData()gibtFetching inventory levels from warehouse...aus, dessenformatReport()gibtFormatting as inventory list.aus und es überschreibtdeliver(), umReport sent to warehouse manager.auszugeben.
main.dart: Importieren Sie Ihre Berichtserstellungsdatei und demonstrieren Sie, wie das Template Method Pattern die Algorithmusstruktur konsistent hält und gleichzeitig Anpassungen ermöglicht. Erstellen Sie einenSalesReportund rufen Sie dessen Methodegenerate()auf. Geben Sie dann eine Leerzeile zur Trennung aus. Erstellen Sie einenInventoryReportund rufen Sie ebenfalls dessen Methodegenerate()auf.
Beachten Sie, wie beide Berichte demselben vierstufigen Prozess folgen, aber jeder die Schritte anpasst, die für seinen spezifischen Zweck wichtig sind. Der InventoryReport überschreibt sogar die Standard-Zustellungsmethode, um Berichte an einen anderen Empfänger zu senden!
Erwartete Ausgabe:
Fetching sales data from database...
Analyzing data...
Formatting as bar charts and tables.
Report delivered via email.
Fetching inventory levels from warehouse...
Analyzing data...
Formatting as inventory list.
Report sent to warehouse manager.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 zu ändern.
Eine abstrakte Klasse definiert eine Template-Methode, die eine Abfolge von Schritten aufruft. Einige Schritte sind abstrakt (müssen von Unterklassen implementiert werden) und einige haben Standardimplementierungen:
abstract class DataProcessor {
// Template-Methode - definiert die Algorithmusstruktur
void process() {
readData();
processData();
saveData();
}
void readData(); // Abstrakt - Unterklassen müssen dies implementieren
void processData(); // Abstrakt - Unterklassen müssen dies implementieren
void saveData() { // Standardimplementierung
print('Data saved to database');
}
}
class CsvProcessor extends DataProcessor {
@override
void readData() => print('Reading CSV file');
@override
void processData() => print('Parsing CSV rows');
}
class JsonProcessor extends DataProcessor {
@override
void readData() => print('Reading JSON file');
@override
void processData() => print('Parsing JSON objects');
@override
void saveData() => print('Saved to cloud storage');
}
Die Template-Methode steuert die Reihenfolge des Workflows. Unterklassen passen einzelne Schritte an, indem sie abstrakte Methoden implementieren und optional Standardimplementierungen überschreiben. Die Struktur des Algorithmus bleibt über alle Implementierungen hinweg konsistent.
Probier es selbst
import 'report_generator.dart';
void main() {
// TODO: Erstelle einen SalesReport und rufe seine generate()-Methode auf
// TODO: Drucke eine Leerzeile zur Trennung
// TODO: Erstelle einen InventoryReport und rufe seine generate()-Methode auf
}
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 DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor12Asynchrone OOP
Futures & async/awaitStreams GrundlagenStream ControllerAsynchrone KonstruktorenAsync in KlassenmethodenZusammenfassung – Data Fetcher15Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterComposite-MusterRepository-Muster