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.
Sfida
FacileCostruiamo 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
TrafficLightStatecon:- 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 GreenYellowState: il colore è"Yellow", l’azione è"Caution", passa a RedGreenState: il colore è"Green", l’azione è"Go", passa a Yellow
Ti servirà una dichiarazione anticipata di
TrafficLight, poiché gli stati fanno riferimento a essa.- Un metodo virtuale puro
TrafficLight.h: crea la classe contesto che mantiene lo stato corrente.La tua classe
TrafficLightdovrebbe contenere unstd::unique_ptr<TrafficLightState>e iniziare per impostazione predefinita nello stato Red. Implementa:setState(std::unique_ptr<TrafficLightState> newState): cambia lo stato correntechange(): delega al metodo change dello stato correntedisplay(): 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
TrafficLighte 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: StopCon l’input 6:
Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: StopNota 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;
}
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
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online