Decorator-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 96 von 110.
Das Decorator-Pattern ermöglicht es Ihnen, Objekten dynamisch neue Verhaltensweisen hinzuzufügen, indem Sie diese in spezielle Objekte, sogenannte Decorator, einhüllen. Im Gegensatz zur Vererbung, die Verhalten zur Kompilierzeit hinzufügt, hängen Decorator zusätzliche Verantwortlichkeiten zur Laufzeit an, ohne die ursprüngliche Klasse zu verändern.
Die Kernidee ist, dass sowohl das ursprüngliche Objekt als auch Dekoratoren dieselbe Schnittstelle implementieren.
Ein Decorator umhüllt das ursprüngliche Objekt, delegiert an dieses und fügt davor oder danach eigenes Verhalten hinzu:
abstract class Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
@override
String getDescription() => 'Simple Coffee';
@override
double getCost() => 2.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Milk';
@override
double getCost() => _coffee.getCost() + 0.5;
}
class SugarDecorator implements Coffee {
final Coffee _coffee;
SugarDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Sugar';
@override
double getCost() => _coffee.getCost() + 0.2;
}
void main() {
Coffee coffee = SimpleCoffee();
coffee = MilkDecorator(coffee);
coffee = SugarDecorator(coffee);
print(coffee.getDescription()); // Simple Coffee, Milk, Sugar
print(coffee.getCost()); // 2.7
}Jeder Decorator umhüllt das vorherige Objekt und erweitert es. Sie können mehrere Decorator in beliebiger Kombination stapeln und so ein flexibles Verhalten erzeugen, ohne für jede mögliche Kombination eine Unterklasse erstellen zu müssen. Dieses Muster ist ideal, wenn Sie Objekten Funktionen hinzufügen müssen, ohne andere Objekte derselben Klasse zu beeinflussen.
Aufgabe
EinfachLassen Sie uns ein Pizza-Bestellsystem mit dem Decorator-Pattern bauen! Sie werden eine Basis-Pizza erstellen, die mit verschiedenen Belägen erweitert werden kann, wobei jeder Belag das vorherige Element umschließt und sowohl die Beschreibung als auch den Preis ergänzt.
Sie werden Ihren Code in zwei Dateien organisieren:
pizza.dart: Diese Datei enthält Ihr Pizza-Interface und alle Decorator-Klassen. Beginnen Sie mit einer abstrakten KlassePizza, die zwei Methoden definiert:String getDescription()unddouble getPrice(). Erstellen Sie dann eine KlassePlainPizza, diePizzaimplementiert - sie gibtPlain Pizzaals Beschreibung zurück und kostet8.0. Erstellen Sie nun drei Decorator-Klassen, die jeweils einePizzaumschließen und diese erweitern:CheeseDecorator- fügt, Cheesezur Beschreibung und1.5zum Preis hinzuPepperoniDecorator- fügt, Pepperonizur Beschreibung und2.0zum Preis hinzuMushroomDecorator- fügt, Mushroomszur Beschreibung und1.25zum Preis hinzu
Pizzaimplementieren, einePizzain seinem Konstruktor akzeptieren und an die umschlossene Pizza delegieren, bevor er seinen eigenen Beitrag hinzufügt.main.dart: Importieren Sie Ihre Pizza-Datei und demonstrieren Sie, wie Decorator gestapelt werden, um eine individuelle Bestellung aufzubauen. Beginnen Sie mit einerPlainPizza, umschließen Sie diese dann mitCheeseDecorator, umschließen Sie das Ergebnis mitPepperoniDecoratorund umschließen Sie schließlich alles mitMushroomDecorator. Geben Sie die Beschreibung der fertigen Pizza aus und geben Sie dannTotal: $gefolgt vom Preis aus.
Beachten Sie, wie jeder Decorator auf dem vorherigen aufbaut - Sie erstellen eine voll belegte Pizza, indem Sie einfache Decorator mit einer einzigen Verantwortlichkeit stapeln!
Erwartete Ausgabe:
Plain Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.75Spickzettel
Das Decorator-Muster fügt Objekten dynamisch neue Verhaltensweisen hinzu, indem es sie in Dekorierer-Objekte einhüllt. Sowohl das ursprüngliche Objekt als auch die Dekorierer implementieren dieselbe Schnittstelle.
Hauptkomponenten:
- Eine abstrakte Klasse oder Schnittstelle, die die gemeinsamen Methoden definiert
- Eine konkrete Klasse, die die Basisfunktionalität implementiert
- Dekorierer-Klassen, die das ursprüngliche Objekt einhüllen und erweitern
Jeder Dekorierer:
- Implementiert dieselbe Schnittstelle wie das Objekt, das er dekoriert
- Hält eine Referenz auf das eingehüllte Objekt
- Delegiert Aufrufe an das eingehüllte Objekt
- Fügt sein eigenes Verhalten vor oder nach der Delegation hinzu
abstract class Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
@override
String getDescription() => 'Simple Coffee';
@override
double getCost() => 2.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Milk';
@override
double getCost() => _coffee.getCost() + 0.5;
}
// Mehrere Dekorierer stapeln
Coffee coffee = SimpleCoffee();
coffee = MilkDecorator(coffee);
coffee = SugarDecorator(coffee);Dekorierer können in beliebiger Kombination gestapelt werden, was flexibles Verhalten ermöglicht, ohne für jede mögliche Kombination Unterklassen erstellen zu müssen.
Probier es selbst
import 'pizza.dart';
void main() {
// TODO: Erstelle eine PlainPizza
// TODO: Umhülle sie mit CheeseDecorator
// TODO: Umhülle das mit PepperoniDecorator
// TODO: Umhülle alles mit MushroomDecorator
// TODO: Gib die Beschreibung der fertigen Pizza aus
// TODO: Gib "Total: $" gefolgt vom Preis aus
}
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