Menu
Coddy logo textTech

Pattern State

Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 100 di 104.

Il pattern State permette a un oggetto di cambiare comportamento quando cambia il suo stato interno, facendo sembrare che l'oggetto abbia cambiato classe. Invece di usare istruzioni condizionali complesse per gestire stati diversi, incapsuli ogni stato in una classe separata.

Il modello è composto da un Contesto che mantiene un riferimento allo stato corrente, da un’interfaccia State che definisce il comportamento specifico di ciascuno stato e da Stati concreti che implementano il comportamento di ogni stato:

#include <iostream>
#include <memory>

class Document;  // Dichiarazione anticipata

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

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

// Stati concreti
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>()) {}

Ogni stato gestisce l'azione publish() in modo diverso ed è responsabile della transizione allo stato successivo. Document non deve conoscere la logica di transizione: si limita a delegare allo stato corrente. Questo elimina le istruzioni switch di grandi dimensioni e rende semplice aggiungere nuovi stati.

Usa State quando il comportamento di un oggetto dipende fortemente dal suo stato e ci sono molte istruzioni condizionali che variano in base allo stato dell'oggetto.

challenge icon

Sfida

Facile

Costruiamo un controller del semaforo usando il pattern State. Creerai un sistema in cui un semaforo passa ciclicamente attraverso diversi stati (Red, Yellow, Green), e ogni stato determina cosa succede quando il semaforo cambia e quale messaggio viene visualizzato. Questo è un classico esempio del pattern State: il comportamento del semaforo dipende interamente dal suo stato corrente.

Organizzerai il codice in tre file:

  • TrafficLightState.h: definisci l’interfaccia dello stato e tutte le classi concrete degli stati.

    Crea una classe astratta TrafficLightState con:

    • Un metodo virtuale puro change(TrafficLight& light) che gestisce il passaggio allo stato successivo
    • Un metodo virtuale puro getColor() che restituisce il colore corrente come stringa
    • Un metodo virtuale puro getAction() che restituisce ciò che i conducenti dovrebbero fare (ad esempio, "Stop", "Caution", "Go")
    • Un distruttore virtuale

    Implementa tre stati concreti:

    • RedState: il colore è "Red", l’azione è "Stop", passa a Green
    • YellowState: il colore è "Yellow", l’azione è "Caution", passa a Red
    • GreenState: il colore è "Green", l’azione è "Go", passa a Yellow

    Ti servirà una dichiarazione anticipata di TrafficLight, poiché gli stati fanno riferimento a essa.

  • TrafficLight.h: crea la classe contesto che mantiene lo stato corrente.

    La tua classe TrafficLight dovrebbe contenere un std::unique_ptr<TrafficLightState> e iniziare per impostazione predefinita nello stato Red. Implementa:

    • setState(std::unique_ptr<TrafficLightState> newState): cambia lo stato corrente
    • change(): delega al metodo change dello stato corrente
    • display(): stampa lo stato del semaforo nel formato: [Color]: [Action]
  • main.cpp: mostra il semaforo mentre passa ciclicamente attraverso gli stati.

    Leggi un input: il numero di cambi di stato da eseguire (intero).

    Crea un TrafficLight e visualizzane lo stato iniziale. Poi esegui il numero specificato di cambi, visualizzando lo stato dopo ogni cambio.

Ad esempio, con l’input 3:

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

Con l’input 6:

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

Nota come il semaforo passa ciclicamente attraverso i suoi stati secondo uno schema prevedibile: Red → Green → Yellow → Red. Ogni stato sa quale stato viene dopo e gestisce la propria transizione. La classe TrafficLight non ha bisogno di alcuna logica condizionale per determinare cosa succede. Delega semplicemente allo stato in cui si trova in quel momento.

Provalo tu

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

using namespace std;

int main() {
    // Leggi il numero di cambiamenti di stato
    int numChanges;
    cin >> numChanges;
    
    // TODO: Crea un oggetto TrafficLight
    
    // TODO: Mostra lo stato iniziale
    
    // TODO: Esegui il numero specificato di cambiamenti
    // Dopo ogni cambiamento, mostra lo stato corrente
    
    return 0;
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C++ online