Template-Method-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy. Lektion 97 von 110.
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 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 (und von Unterklassen implementiert werden müssen) und einige Standardimplementierungen besitzen:
abstract class DataProcessor {
// Template-Methode - definiert die Algorithmusstruktur
void process() {
readData();
processData();
saveData();
}
void readData(); // Abstrakt - Unterklassen müssen implementieren
void processData(); // Abstrakt - Unterklassen müssen 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();
// CSV-Datei wird gelesen
// CSV-Zeilen werden geparst
// Daten in Datenbank gespeichert
var json = JsonProcessor();
json.process();
// JSON-Datei wird gelesen
// JSON-Objekte werden geparst
// In Cloud-Speicher gespeichert
}Die Methode process() ist die Vorlage – sie definiert die Reihenfolge der Vorgänge. Unterklassen implementieren readData() und processData() unterschiedlich und können optional saveData() überschreiben. Der entscheidende Vorteil besteht darin, dass die Struktur des Algorithmus über alle Implementierungen hinweg konsistent bleibt und gleichzeitig Flexibilität bei den einzelnen Schritten ermöglicht wird.
Aufgabe
EinfachLass uns ein System zur Berichtserstellung mit dem Template-Method-Muster erstellen! Du wirst ein Framework erstellen, in dem verschiedene Berichtstypen derselben übergeordneten Struktur folgen, aber jeder Berichtstyp bestimmte Schritte wie das Erfassen der Daten und das Formatieren der Ausgabe individuell gestaltet.
Du wirst deinen Code in zwei Dateien organisieren:
report_generator.dart: Diese Datei enthält deine abstrakte Basisklasse und konkrete Berichtsimplementierungen. Erstelle eine abstrakte KlasseReportGeneratormit einer Template-Methode namensgenerate(), die den Ablauf der Berichtserstellung in dieser Reihenfolge definiert:fetchData(),analyzeData(),formatReport()unddeliver(). Die MethodenfetchData()undformatReport()sollten abstrakt sein (jeder Berichtstyp behandelt sie anders), währendanalyzeData()standardmäßigAnalyzing data...ausgeben unddeliver()standardmäßigReport delivered via email.ausgeben sollte. Erstelle anschließend zwei konkrete Berichtsklassen:SalesReport– seinfetchData()gibtFetching sales data from database...aus und seinformatReport()gibtFormatting as bar charts and tables.aus.InventoryReport– seinfetchData()gibtFetching inventory levels from warehouse...aus, seinformatReport()gibtFormatting as inventory list.aus, und es überschreibtdeliver(), umReport sent to warehouse manager.auszugeben.
main.dart: Importiere deine Datei für die Berichtserstellung und demonstriere, wie das Template-Method-Muster die Algorithmusstruktur konsistent hält und gleichzeitig Anpassungen ermöglicht. Erstelle einenSalesReportund rufe seine Methodegenerate()auf. Gib anschließend zur Trennung eine leere Zeile aus. Erstelle einenInventoryReportund rufe ebenfalls seine Methodegenerate()auf.
Beachte, wie beide Berichte demselben vierstufigen Prozess folgen, aber jeweils die für ihren spezifischen Zweck wichtigen Schritte anpassen. Der InventoryReport überschreibt sogar die standardmäßige Zustellmethode, 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.Probier es selbst
import 'report_generator.dart';
void main() {
// TODO: Erstelle einen SalesReport und rufe seine generate()-Methode auf
// TODO: Gib eine leere Zeile zur Trennung aus
// 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 Schlüsselwort thisMethodenInstanzvariablenGrundlagen von KonstruktorenRückblick – Einfacher Taschenrechner4Null-Sicherheit
Einführung in die Null-SicherheitNullable vs. Non-NullableDie Operatoren ? und !Late-Schlüsselwort und Null-SicherheitNull-bewusste OperatorenNull-Sicherheit in KlassenRückblick – Benutzers профilsystem7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrere InterfacesZusammenfassung – Formrechner10Collections & Generics
Übersicht über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische EinschränkungenIterable & IteratorRückblick – Generischer Speicher13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungErweiterungsmethodenAufrufbare KlassenVersiegelte Klassen (Dart 3)Records (Dart 3)Muster & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
StandardkonstruktorBenannte KonstruktorenInitialisierungslistenKonstantenkonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenRückblick – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _-Präfix-KonventionBibliotheksweite SichtbarkeitTiefergehende Getter & SetterInformationsverbergungRückblick – Studierendendaten8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas Schlüsselwort „on“ in MixinsMixin vs. VererbungMixin vs. SchnittstelleRückblick – Tiersystem11Spezielle Methoden
toString()-ÜberschreibunghashCode- & ==-ÜberschreibungComparable-Schnittstellecall()-MethodenoSuchMethod-ÜberschreibungWiederholung – Benutzerdefinierte Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFabrikmusterBeobachtermusterStrategiemuster17Abschließende Herausforderungen
E-Learning-PlattformBankingsystemSpielcharaktersystemFahrzeugvermietung3Klassen-Eigenschaften
Instanz- vs. statische MemberFinal- und Const-FelderLate-VariablenStatische Methoden und FelderGetter und SetterRückblick – Bankkontoverwaltung6Vererbung
Grundlegende VererbungDas Schlüsselwort superMethodenüberschreibungDie Annotation @overrideDas Schlüsselwort final für KlassenKonstruktoren und VererbungZusammenfassung – Mitarbeiterhierarchie9Polymorphismus
Grundlagen des PolymorphismusPolymorphismus über InterfacesLaufzeit-TypüberprüfungDie Operatoren is & asCovariant-SchlüsselwortZusammenfassung – Zahlungsprozessor12Asynchrone OOP
Futures & async/awaitStream-GrundlagenStream-ControllerAsynchrone KonstruktorenAsync in KlassenmethodenZusammenfassung – Datenabruf15Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterState-MusterComposite-MusterRepository-MusterÜbe selbstständig: Online-Dart-Compiler