Menu
Coddy logo textTech

Wzorzec stanu

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 100 z 104.

Wzorzec stanu pozwala obiektowi zmieniać zachowanie, gdy zmienia się jego stan wewnętrzny, sprawiając wrażenie, jakby obiekt zmienił swoją klasę. Zamiast używać złożonych instrukcji warunkowych do obsługi różnych stanów, enkapsulujesz każdy stan w osobnej klasie.

Wzorzec składa się z Kontekstu, który przechowuje odwołanie do bieżącego stanu, interfejsu Stanu, który definiuje zachowanie właściwe dla danego stanu, oraz Stanów konkretnych, które implementują zachowanie dla każdego stanu:

#include <iostream>
#include <memory>

class Document;  // Deklaracja wstępna

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

// Kontekst
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(); }
};

// Konkretne stany
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>()) {}

Każdy stan obsługuje działanie publish() inaczej i odpowiada za przejście do następnego stanu. Document nie musi znać logiki przejść — po prostu deleguje obsługę do bieżącego stanu. Eliminuje to rozbudowane instrukcje switch i ułatwia dodawanie nowych stanów.

Użyj wzorca State, gdy zachowanie obiektu w dużym stopniu zależy od jego stanu i masz wiele instrukcji warunkowych, które przełączają się w zależności od stanu obiektu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy sterownik sygnalizacji świetlnej z użyciem wzorca State. Utworzysz system, w którym sygnalizacja świetlna przechodzi przez różne stany (czerwony, żółty, zielony), a każdy stan określa, co dzieje się po zmianie światła i jaki komunikat jest wyświetlany. To klasyczny przykład wzorca State: zachowanie sygnalizacji świetlnej zależy całkowicie od jej bieżącego stanu.

Rozmieścisz kod w trzech plikach:

  • TrafficLightState.h: Zdefiniuj interfejs stanu i wszystkie konkretne klasy stanów.

    Utwórz abstrakcyjną klasę TrafficLightState zawierającą:

    • Czysto wirtualną metodę change(TrafficLight& light), która obsługuje przejście do następnego stanu
    • Czysto wirtualną metodę getColor(), która zwraca bieżący kolor jako ciąg znaków
    • Czysto wirtualną metodę getAction(), która zwraca informację o tym, co powinni zrobić kierowcy (np. "Stop", "Caution", "Go")
    • Wirtualny destruktor

    Zaimplementuj trzy konkretne stany:

    • RedState: kolor to "Red", akcja to "Stop", przechodzi w stan zielony
    • YellowState: kolor to "Yellow", akcja to "Caution", przechodzi w stan czerwony
    • GreenState: kolor to "Green", akcja to "Go", przechodzi w stan żółty

    Potrzebujesz deklaracji wstępnej klasy TrafficLight, ponieważ odwołują się do niej stany.

  • TrafficLight.h: Utwórz klasę kontekstu, która przechowuje bieżący stan.

    Klasa TrafficLight powinna przechowywać std::unique_ptr<TrafficLightState> i domyślnie rozpoczynać w stanie czerwonym. Zaimplementuj:

    • setState(std::unique_ptr<TrafficLightState> newState): zmienia bieżący stan
    • change(): deleguje do metody change bieżącego stanu
    • display(): wyświetla stan sygnalizacji w formacie: [Color]: [Action]
  • main.cpp: Zademonstruj przechodzenie sygnalizacji świetlnej przez kolejne stany.

    Wczytaj jedną wartość: liczbę zmian stanu do wykonania (liczbę całkowitą).

    Utwórz obiekt TrafficLight i wyświetl jego stan początkowy. Następnie wykonaj podaną liczbę zmian, wyświetlając stan po każdej z nich.

Na przykład dla wejścia 3:

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

Dla wejścia 6:

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

Zwróć uwagę, jak sygnalizacja świetlna przechodzi przez swoje stany według przewidywalnego schematu: czerwony → zielony → żółty → czerwony. Każdy stan wie, który stan następuje po nim, i sam obsługuje własne przejście. Klasa TrafficLight nie potrzebuje żadnej logiki warunkowej, aby określić, co się stanie. Po prostu deleguje działanie do stanu, w którym aktualnie się znajduje.

Spróbuj swoich sił

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

using namespace std;

int main() {
    // Wczytaj liczbę zmian stanu
    int numChanges;
    cin >> numChanges;
    
    // TODO: Utwórz obiekt TrafficLight
    
    // TODO: Wyświetl stan początkowy
    
    // TODO: Wykonaj podaną liczbę zmian
    // Po każdej zmianie wyświetl bieżący stan
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online