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: einem State-Interface, das zustandsspezifisches Verhalten definiert, konkreten State-Klassen, die dieses Verhalten implementieren, und einer Kontext-Klasse, die eine Referenz auf den aktuellen Zustand hält:
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
EinfachLassen Sie uns ein Ampelsystem mit dem State-Pattern erstellen. Ampeln durchlaufen verschiedene Zustände (Rot, Gelb, Grün), und jeder Zustand bestimmt, was passiert, wenn die Ampel umschaltet. Anstatt komplexe Bedingungen zu verwenden, kapseln Sie jeden Ampelzustand in einer eigenen Klasse.
Sie organisieren Ihren Code in drei Dateien:
TrafficLightState.cs: Definieren Sie einITrafficLightState-Interface im NamespaceTrafficmit einer MethodeChange(TrafficLight light). Erstellen Sie dann drei konkrete Zustandsklassen:RedState- wennChangeaufgerufen wird, gibt sieRed - Stop! Changing to Green...aus und versetzt die Ampel in denGreenStateGreenState- gibtGreen - Go! Changing to Yellow...aus und wechselt in denYellowStateYellowState- gibtYellow - Caution! Changing to Red...aus und wechselt in denRedState
TrafficLight.cs: Erstellen Sie eine KlasseTrafficLightim selben Namespace. Dies ist Ihre Kontextklasse, die den aktuellen Zustand verwaltet. Sie sollte standardmäßig imRedStatestarten und Folgendes enthalten:- Eine Methode
SetState(ITrafficLightState state), die von Zustandsobjekten verwendet wird, um Übergänge auszulösen - Eine Methode
Change(), die den Aufruf an dieChange-Methode des aktuellen Zustands delegiert
- Eine Methode
Program.cs: Führen Sie alles zusammen, indem Sie eineTrafficLighterstellen und die Zustandsänderungen basierend auf der Eingabe durchlaufen.
Sie erhalten eine Eingabe:
- Die Anzahl der Male, die die Ampel umgeschaltet werden soll (eine Ganzzahl)
Erstellen Sie eine TrafficLight (die bei Rot beginnt) und rufen Sie Change() so oft wie angegeben auf.
Wenn die Eingabe beispielsweise 3 ist, 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 ist, durchläuft die Ampel den Zyklus erneut:
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...Beachten Sie, dass die Klasse TrafficLight keine Logik darüber enthält, was jede Farbe bedeutet oder was als Nächstes kommt – all dieses Wissen befindet sich in den einzelnen Zustandsklassen. Jeder Zustand ist für sein eigenes Verhalten und für das Auslösen des Übergangs in den nächsten Zustand verantwortlich!
Spickzettel
Das State-Pattern (Zustandsmuster) 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, kapseln Sie jeden Zustand in einer eigenen Klasse und delegieren das Verhalten an das aktuelle Zustandsobjekt.
Das Muster besteht aus drei Komponenten:
- State-Interface: Definiert zustandsspezifisches Verhalten
- Konkrete Zustands-Klassen: Implementieren das Verhalten für jeden Zustand
- Kontext-Klasse: Hält eine Referenz auf den aktuellen Zustand
Beispielhafte Implementierung:
// State-Interface
public interface IDocumentState
{
void Publish(Document doc);
}
// Konkrete Zustands-Klassen
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");
}
}
// Kontext-Klasse
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
// Verwendung
var doc = new Document();
doc.Publish(); // Moving to moderation
doc.Publish(); // Publishing document
doc.Publish(); // Already publishedDer 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. Dies eliminiert ausufernde if-else- oder switch-Anweisungen und macht den Code einfacher zu warten und um neue Zustände zu erweitern.
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. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungDependency Injection Grundlagen11Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterKompositum-Muster3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten