Menu
Coddy logo textTech

Zustandsmuster

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 100 von 104.

Das State pattern ermöglicht es einem Objekt, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert, sodass es den Anschein erweckt, als hätte das Objekt seine Klasse geändert. Anstatt komplexe bedingte Anweisungen zur Behandlung verschiedener Zustände zu verwenden, kapselst du jeden Zustand als separate Klasse.

Das Muster besteht aus einem Kontext, der eine Referenz auf den aktuellen Zustand verwaltet, einer Zustandsschnittstelle, die zustandsspezifisches Verhalten definiert, und konkreten Zuständen, die das Verhalten für jeden Zustand implementieren:

#include <iostream>
#include <memory>

class Document;  // Vorwärtsdeklaration

// Zustandsschnittstelle
class DocumentState {
public:
    virtual void publish(Document& doc) = 0;
    virtual std::string getName() const = 0;
    virtual ~DocumentState() = default;
};

// Kontext
class Document {
    std::unique_ptr<DocumentState> state;
public:
    Document();
    void setState(std::unique_ptr<DocumentState> newState) {
        state = std::move(newState);
    }
    void publish() { state->publish(*this); }
    std::string getStateName() const { return state->getName(); }
};

// Konkrete Zustände
class Draft : public DocumentState {
public:
    void publish(Document& doc) override;
    std::string getName() const override { return "Draft"; }
};

class Review : public DocumentState {
public:
    void publish(Document& doc) override;
    std::string getName() const override { return "Review"; }
};

class Published : public DocumentState {
public:
    void publish(Document& doc) override {
        std::cout << "Already published\n";
    }
    std::string getName() const override { return "Published"; }
};

void Draft::publish(Document& doc) {
    std::cout << "Moving to review\n";
    doc.setState(std::make_unique<Review>());
}

void Review::publish(Document& doc) {
    std::cout << "Publishing document\n";
    doc.setState(std::make_unique<Published>());
}

Document::Document() : state(std::make_unique<Draft>()) {}

Jeder Zustand behandelt die Aktion publish() anders und ist dafür verantwortlich, in den nächsten Zustand überzugehen. Das Document muss die Übergangslogik nicht kennen – es delegiert einfach an den aktuellen Zustand. Dadurch werden umfangreiche Switch-Anweisungen vermieden und das Hinzufügen neuer Zustände wird unkompliziert.

Verwende State, wenn das Verhalten eines Objekts stark von seinem Zustand abhängt und du viele bedingte Anweisungen hast, die je nach Zustand des Objekts wechseln.

challenge icon

Aufgabe

Einfach

Erstellen wir einen Ampel-Controller mithilfe des Zustandsmusters. Du erstellst ein System, in dem eine Ampel verschiedene Zustände (Rot, Gelb, Grün) durchläuft, wobei jeder Zustand festlegt, was passiert, wenn die Ampel wechselt, und welche Meldung angezeigt wird. Dies ist ein klassisches Beispiel für das Zustandsmuster: Das Verhalten der Ampel hängt vollständig von ihrem aktuellen Zustand ab.

Du organisierst deinen Code auf drei Dateien verteilt:

  • TrafficLightState.h: Definiere die Zustandsschnittstelle und alle konkreten Zustandsklassen.

    Erstelle eine abstrakte Klasse TrafficLightState mit:

    • einer rein virtuellen Methode change(TrafficLight& light), die den Übergang zum nächsten Zustand verarbeitet
    • einer rein virtuellen Methode getColor(), die die aktuelle Farbe als Zeichenkette zurückgibt
    • einer rein virtuellen Methode getAction(), die zurückgibt, was die Fahrer tun sollen (z. B. "Stop", "Caution", "Go")
    • einem virtuellen Destruktor

    Implementiere drei konkrete Zustände:

    • RedState: Farbe ist "Red", Aktion ist "Stop", wechselt zu Green
    • YellowState: Farbe ist "Yellow", Aktion ist "Caution", wechselt zu Red
    • GreenState: Farbe ist "Green", Aktion ist "Go", wechselt zu Yellow

    Du benötigst eine Vorwärtsdeklaration für TrafficLight, da die Zustände darauf verweisen.

  • TrafficLight.h: Erstelle die Kontextklasse, die den aktuellen Zustand verwaltet.

    Deine Klasse TrafficLight sollte einen std::unique_ptr<TrafficLightState> enthalten und standardmäßig im Zustand Red starten. Implementiere:

    • setState(std::unique_ptr<TrafficLightState> newState): ändert den aktuellen Zustand
    • change(): delegiert an die change-Methode des aktuellen Zustands
    • display(): gibt den Status der Ampel im Format [Color]: [Action] aus
  • main.cpp: Veranschauliche, wie die Ampel die Zustände durchläuft.

    Lies eine Eingabe ein: die Anzahl der auszuführenden Zustandsänderungen (Ganzzahl).

    Erstelle eine TrafficLight und zeige ihren Anfangszustand an. Führe anschließend die angegebene Anzahl von Änderungen durch und zeige den Zustand nach jeder Änderung an.

Bei einer Eingabe von 3:

Red: Stop
Green: Go
Yellow: Caution
Red: Stop

Bei einer Eingabe von 6:

Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: Stop

Beachte, wie die Ampel ihre Zustände in einem vorhersehbaren Muster durchläuft: Red → Green → Yellow → Red. Jeder Zustand weiß, welcher Zustand als Nächstes folgt, und verarbeitet seinen eigenen Übergang. Die Klasse TrafficLight benötigt keine bedingte Logik, um zu bestimmen, was passiert. Sie delegiert einfach an den Zustand, in dem sie sich gerade befindet.

Probier es selbst

#include <iostream>
#include "TrafficLight.h"

using namespace std;

int main() {
    // Lies die Anzahl der Zustandsänderungen
    int numChanges;
    cin >> numChanges;
    
    // TODO: Erstelle ein TrafficLight-Objekt
    
    // TODO: Zeige den Anfangszustand an
    
    // TODO: Führe die angegebene Anzahl von Änderungen durch
    // Nach jeder Änderung den aktuellen Zustand anzeigen
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C++-Compiler