State-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy. Lektion 98 von 110.
Das State-Pattern ermöglicht es einem Objekt, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert, sodass es so aussieht, als ob das Objekt seine Klasse gewechselt hätte. Anstatt komplexe Bedingungen zu verwenden, um verschiedene Zustände zu handhaben, kapseln Sie jeden Zustand in seiner eigenen Klasse und delegieren das Verhalten an das aktuelle Zustandsobjekt.
Das Muster besteht aus einem Kontext, der eine Referenz auf den aktuellen Zustand verwaltet, und Zustandsklassen, die das Verhalten für jeden möglichen Zustand definieren. Wenn sich der Zustand des Kontexts ändert, ändert sich sein Verhalten automatisch:
abstract class DocumentState {
void publish(Document doc);
String getStatus();
}
class DraftState implements DocumentState {
@override
void publish(Document doc) {
print('Moving to review...');
doc.setState(ReviewState());
}
@override
String getStatus() => 'Draft';
}
class ReviewState implements DocumentState {
@override
void publish(Document doc) {
print('Document published!');
doc.setState(PublishedState());
}
@override
String getStatus() => 'Under Review';
}
class PublishedState implements DocumentState {
@override
void publish(Document doc) {
print('Already published.');
}
@override
String getStatus() => 'Published';
}
class Document {
DocumentState _state = DraftState();
void setState(DocumentState state) => _state = state;
void publish() => _state.publish(this);
String getStatus() => _state.getStatus();
}
void main() {
var doc = Document();
print(doc.getStatus()); // Entwurf
doc.publish(); // Wechsel zur Überprüfung...
print(doc.getStatus()); // In Überprüfung
doc.publish(); // Dokument veröffentlicht!
print(doc.getStatus()); // Veröffentlicht
}Jede Zustandsklasse behandelt die publish()-Aktion unterschiedlich und kann das Dokument in den nächsten Zustand überführen. Die Document-Klasse benötigt keine Bedingungen, um ihren aktuellen Zustand zu prüfen – sie delegiert einfach an das jeweils aktive Zustandsobjekt. Dieses Muster ist ideal für Objekte mit unterschiedlichen Verhaltensmodi, wie die Auftragsabwicklung, Mediaplayer oder Workflow-Systeme.
Aufgabe
EinfachLassen Sie uns ein Ampelsystem mit dem State-Pattern bauen! Sie werden eine Ampel erstellen, die verschiedene Zustände durchläuft, wobei jeder Zustand die Farbe der Ampel bestimmt und festlegt, wie sie in den nächsten Zustand übergeht.
Sie werden Ihren Code in zwei Dateien organisieren:
traffic_light.dart: Diese Datei enthält Ihre Zustandsklassen und den Ampel-Kontext. Erstellen Sie eine abstrakte KlasseTrafficLightStatemit zwei Methoden:String getColor(), die die aktuelle Ampelfarbe zurückgibt, undvoid next(TrafficLight light), die die Ampel in ihren nächsten Zustand überführt. Implementieren Sie drei konkrete Zustandsklassen:RedState- gibt'RED'als Farbe zurück und gibt'Stop! Light is red.'aus, wennnext()aufgerufen wird, und wechselt dann zuGreenStateGreenState- gibt'GREEN'als Farbe zurück und gibt'Go! Light is green.'aus, wennnext()aufgerufen wird, und wechselt dann zuYellowStateYellowState- gibt'YELLOW'als Farbe zurück und gibt'Caution! Light is yellow.'aus, wennnext()aufgerufen wird, und wechselt dann zuRedState
TrafficLight-Klasse (den Kontext), die imRedStatestartet. Sie sollte einesetState()-Methode haben, um den aktuellen Zustand zu ändern, einegetColor()-Methode, die an den aktuellen Zustand delegiert, und einechange()-Methode, dienext()auf dem aktuellen Zustand aufruft.main.dart: Importieren Sie Ihre Ampel-Datei und demonstrieren Sie das State-Pattern in Aktion. Erstellen Sie eineTrafficLightund geben Sie deren Anfangsfarbe aus. Rufen Sie dann dreimalchange()auf und geben Sie nach jeder Änderung die Farbe aus. Dies zeigt, wie die Ampel alle Zustände durchläuft und zum Anfang zurückkehrt.
Beachten Sie, dass die TrafficLight-Klasse keine Bedingungen benötigt, um das Verhalten zu bestimmen – jeder Zustand kennt seine eigene Farbe und weiß, welcher Zustand als Nächstes kommt. Die Ampel delegiert einfach an den Zustand, der gerade aktiv ist!
Erwartete Ausgabe:
RED
Stop! Light is red.
GREEN
Go! Light is green.
YELLOW
Caution! Light is yellow.
REDProbier es selbst
import 'traffic_light.dart';
void main() {
// TODO: Erstelle eine TrafficLight-Instanz
// TODO: Gib die Ausgangsfarbe aus
// TODO: Rufe change() auf und gib die Farbe aus (3 Mal, um alle Zustände zu durchlaufen)
// Erster Wechsel: RED -> GREEN
// Zweiter Wechsel: GREEN -> YELLOW
// Dritter Wechsel: YELLOW -> RED
}
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