Das State Pattern ist ein Verhaltensmuster, das es einem Objekt ermöglicht, sein Verhalten basierend auf seinem Zustand zu ändern. Statt Bedingungen im Code zu überprüfen, delegiert das Objekt das Verhalten an spezifische Zustandsobjekte. Dieses Muster hilft, den Code klarer und wartbarer zu gestalten, da es den Zustand eines Objekts in eigenständige Klassen kapselt. Auch wird die Implementierung oft State-Machine genannt.
Was ist das State Pattern?
Das State Pattern löst das Problem der Zustandsänderungen in einem Objekt, indem es das Verhalten in separate Zustandsklassen auslagert. Diese Zustandsklassen implementieren jeweils unterschiedliche Verhaltensweisen, die zur aktuellen Situation des Objekts passen. Das Objekt kann den Zustand jederzeit ändern, und es verhält sich entsprechend.
Komponenten des State Patterns
- Context: Das Context-Objekt verwaltet den aktuellen Zustand. Es delegiert das Verhalten an das aktuelle State-Objekt.
- State: Das State-Interface definiert allgemeine Methoden, die von konkreten Zuständen implementiert werden.
- ConcreteState: Diese Klassen implementieren das State-Interface und definieren das Verhalten für verschiedene Zustände.
Beispiel des State Patterns in C++
Im folgenden Beispiel wird ein einfacher Zustand eines Verkehrslichts modelliert. Das Verkehrslicht wechselt zwischen verschiedenen Zuständen wie „grün“, „gelb“ und „rot“. Je nach Zustand verhält sich das Verkehrslicht unterschiedlich.
#include <iostream>
#include <memory>
// State: Abstrakte Zustandsschnittstelle
class TrafficLightState {
public:
virtual void handle() = 0;
virtual ~TrafficLightState() = default;
};
// ConcreteState: Zustand für grünes Licht
class GreenState : public TrafficLightState {
public:
void handle() override {
std::cout << "Das Licht ist grün. Fahrzeugverkehr kann passieren." << std::endl;
}
};
// ConcreteState: Zustand für gelbes Licht
class YellowState : public TrafficLightState {
public:
void handle() override {
std::cout << "Das Licht ist gelb. Bitte anhalten." << std::endl;
}
};
// ConcreteState: Zustand für rotes Licht
class RedState : public TrafficLightState {
public:
void handle() override {
std::cout << "Das Licht ist rot. Fahrzeuge müssen anhalten." << std::endl;
}
};
// Context: Das Verkehrslichtobjekt, das den Zustand verwaltet
class TrafficLight {
private:
std::shared_ptr<TrafficLightState> state;
public:
TrafficLight() : state(std::make_shared<RedState>()) {}
void setState(std::shared_ptr<TrafficLightState> newState) {
state = newState;
}
void changeState() {
state->handle();
}
};
// Client-Code
int main() {
TrafficLight trafficLight;
// Das Licht ist zu Beginn rot
trafficLight.changeState();
// Zustand auf grün setzen
trafficLight.setState(std::make_shared<GreenState>());
trafficLight.changeState();
// Zustand auf gelb setzen
trafficLight.setState(std::make_shared<YellowState>());
trafficLight.changeState();
// Zustand auf rot setzen
trafficLight.setState(std::make_shared<RedState>());
trafficLight.changeState();
return 0;
}
Erklärung des C++-Beispiels
- TrafficLightState: Das
TrafficLightState
ist die abstrakte Basisklasse, die das Interface für alle konkreten Zustände definiert. Es enthält die Methodehandle()
, die in den konkreten Zuständen implementiert wird. - ConcreteState: Die Klassen
GreenState
,YellowState
undRedState
sind konkrete Implementierungen des Zustands. Jede dieser Klassen überschreibt die Methodehandle()
und definiert das spezifische Verhalten für den jeweiligen Zustand. - TrafficLight: Die Klasse
TrafficLight
ist der Kontext. Sie verwaltet den aktuellen Zustand des Lichts und delegiert das Verhalten an das entsprechendeTrafficLightState
-Objekt. Sie bietet auch eine MethodesetState()
, mit der der Zustand geändert werden kann. - Client: Der Client erstellt das
TrafficLight
-Objekt und steuert den Zustand des Lichts. Er ändert den Zustand nacheinander von „rot“ zu „grün“ und dann zu „gelb“, wobei das Verhalten jeweils angepasst wird.
Vorteile des State Patterns
- Erhöhte Klarheit: Das Verhalten wird direkt in die jeweiligen Zustände ausgelagert, was den Code klarer und übersichtlicher macht.
- Vermeidung von komplexen Bedingungen: Anstatt komplexe
if
-Bedingungen zu verwenden, delegiert das Objekt das Verhalten direkt an den aktuellen Zustand. - Erweiterbarkeit: Neue Zustände können einfach durch die Implementierung zusätzlicher
State
-Klassen hinzugefügt werden, ohne den Code im Kontext zu ändern. - Erleichterte Wartung: Jeder Zustand ist in einer eigenen Klasse gekapselt, was das System modularer und leichter wartbar macht.
Nachteile des State Patterns
- Erhöhte Anzahl von Klassen: Da jeder Zustand in einer eigenen Klasse implementiert wird, kann die Anzahl der Klassen im System steigen, besonders bei vielen Zuständen.
- Komplexität bei Zustandsübergängen: Wenn Zustände komplexe Übergänge erfordern, kann das Pattern mehr Aufwand erfordern. Die Verwaltung dieser Übergänge könnte unübersichtlich werden.
- Wiederverwendbarkeit: Die Zustandsklassen sind oft stark an das spezifische Problem gebunden und können in anderen Kontexten weniger nützlich sein.
Fazit
Das State Pattern ist ein sehr effektives Muster, um Zustandsänderungen innerhalb eines Objekts zu verwalten. Es sorgt dafür, dass der Zustand eines Objekts die Verantwortung für das Verhalten übernimmt, und entkoppelt so die Logik des Verhaltens von der übrigen Systemlogik. Durch die Implementierung des Zustands als separate Klassen wird der Code modular und erweiterbar. Das Beispiel zeigt, wie das Muster in einem Verkehrslichtsystem verwendet werden kann. Das Muster eignet sich hervorragend, wenn das Verhalten eines Objekts stark von seinem Zustand abhängt und häufige Zustandsänderungen auftreten.
Zurück zur Übersicht der Pattern: Liste der Design-Pattern