Zustandsmuster
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 60 von 70.
Das State-Pattern ist ein Verhaltensmuster, das es einem Objekt ermöglicht, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert. Anstatt komplexe bedingte Anweisungen 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 drei Komponenten: einer Zustandsschnittstelle, die zustandsspezifisches Verhalten definiert, konkreten Zustandsklassen, die dieses Verhalten implementieren, und einer Kontextklasse, die eine Referenz auf den aktuellen Zustand verwaltet:
public interface IDocumentState
{
void Publish(Document doc);
}
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}Der Kontext hält den aktuellen Zustand und delegiert Aktionen an diesen. Zustandsobjekte können Übergänge auslösen, indem sie SetState() auf dem Kontext aufrufen:
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
var doc = new Document();
doc.Publish(); // Wechsel zur Moderation
doc.Publish(); // Dokument wird veröffentlicht
doc.Publish(); // Bereits veröffentlichtDas State-Pattern eliminiert ausufernde if-else- oder switch-Anweisungen, die den aktuellen Zustand prüfen. Jede Zustands-Klasse behandelt nur ihr eigenes Verhalten, wodurch der Code einfacher zu warten und um neue Zustände zu erweitern ist.
Aufgabe
EinfachErstellen wir ein Ampelsystem mithilfe des Zustandsmusters. Ampeln durchlaufen verschiedene Zustände (rot, gelb, grün), und jeder Zustand bestimmt, was passiert, wenn sich das Licht ändert. Anstatt komplexe Bedingungen zu verwenden, kapselst du jeden Lichtzustand in einer eigenen Klasse.
Du organisierst deinen Code über drei Dateien:
TrafficLightState.cs: Definiere einITrafficLightState-Interface imTraffic-Namespace mit einer MethodeChange(TrafficLight light). Erstelle anschließend drei konkrete Zustandsklassen:RedState– wennChangeaufgerufen wird, gibt die KlasseRed - Stop! Changing to Green...aus und versetzt die Ampel in den ZustandGreenStateGreenState– gibtGreen - Go! Changing to Yellow...aus und wechselt in den ZustandYellowStateYellowState– gibtYellow - Caution! Changing to Red...aus und wechselt in den ZustandRedState
TrafficLight.cs: Erstelle eineTrafficLight-Klasse im selben Namespace. Dies ist deine Kontextklasse, die den aktuellen Zustand verwaltet. Sie soll standardmäßig im ZustandRedStatestarten und Folgendes enthalten:- Eine Methode
SetState(ITrafficLightState state), die von Zustandsobjekten verwendet wird, um Übergänge auszulösen - Eine Methode
Change(), die an dieChange-Methode des aktuellen Zustands delegiert
- Eine Methode
Program.cs: Führe alles zusammen, indem du eineTrafficLighterstellst und abhängig von der Eingabe durch die Zustandsänderungen wechselst.
Du erhältst eine Eingabe:
- Die Anzahl der Änderungen der Ampel (eine Ganzzahl)
Erstelle eine TrafficLight (die bei Rot startet) und rufe Change() so oft wie angegeben auf.
Wenn die Eingabe beispielsweise 3 lautet, sollte die Ausgabe wie folgt aussehen:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...Wenn die Eingabe 5 lautet, durchläuft die Ampel wieder alle Zustände:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...Beachte, dass die Klasse TrafficLight keine Logik darüber enthält, was die einzelnen Farben bedeuten oder welcher Zustand als Nächstes kommt – dieses gesamte Wissen befindet sich in den einzelnen Zustandsklassen. Jeder Zustand ist für sein eigenes Verhalten und das Auslösen des Übergangs zum nächsten Zustand verantwortlich!
Probier es selbst
using System;
using Traffic;
class Program
{
public static void Main(String[] args)
{
// Lies die Anzahl der Male, die die Ampel umgeschaltet werden soll
int n = Convert.ToInt32(Console.ReadLine());
// TODO: Erstelle eine TrafficLight-Instanz (startet standardmäßig im RedState)
// TODO: Rufe die Methode Change() n-mal mithilfe einer Schleife 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 DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungGrundlagen der Dependency Injection11Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterVorlagenmethodenmusterZustandsmusterKompositumsmuster3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler