Patron État
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 100 sur 104.
Le patron State permet à un objet de modifier son comportement lorsque son état interne change, donnant l'impression que l'objet a changé de classe. Au lieu d'utiliser des instructions conditionnelles complexes pour gérer différents états, vous encapsulez chaque état dans une classe distincte.
Le patron se compose d'un Contexte qui maintient une référence vers l'état actuel, d'une interface État définissant le comportement spécifique à l'état, et d'États concrets qui implémentent le comportement pour chaque état :
#include <iostream>
#include <memory>
class Document; // Déclaration anticipée
// Interface d'état
class DocumentState {
public:
virtual void publish(Document& doc) = 0;
virtual std::string getName() const = 0;
virtual ~DocumentState() = default;
};
// Contexte
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(); }
};
// États concrets
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>()) {}Chaque état gère l'action publish() différemment et est responsable de la transition vers l'état suivant. Le Document n'a pas besoin de connaître la logique de transition - il délègue simplement à l'état actuel. Cela élimine les instructions switch volumineuses et rend l'ajout de nouveaux états simple.
Utilisez le patron State lorsque le comportement d'un objet dépend fortement de son état et que vous avez de nombreuses instructions conditionnelles qui basculent en fonction de l'état de l'objet.
Défi
FacileConstruisons un Contrôleur de Feux de Signalisation en utilisant le patron de conception State (État). Vous allez créer un système où un feu de signalisation cycle à travers différents états (Red, Yellow, Green), chaque état déterminant ce qui se passe lorsque le feu change et quel message est affiché. C'est un exemple classique du patron State : le comportement du feu de signalisation dépend entièrement de son état actuel.
Vous organiserez votre code sur trois fichiers :
TrafficLightState.h: Définissez l'interface d'état et toutes les classes d'état concrètes.Créez une classe abstraite
TrafficLightStateavec :- Une méthode virtuelle pure
change(TrafficLight& light)qui gère la transition vers l'état suivant - Une méthode virtuelle pure
getColor()qui retourne la couleur actuelle sous forme de chaîne de caractères - Une méthode virtuelle pure
getAction()qui retourne ce que les conducteurs doivent faire (par exemple,"Stop","Caution","Go") - Un destructeur virtuel
Implémentez trois états concrets :
RedState— la couleur est"Red", l'action est"Stop", change vers GreenYellowState— la couleur est"Yellow", l'action est"Caution", change vers RedGreenState— la couleur est"Green", l'action est"Go", change vers Yellow
Vous aurez besoin d'une déclaration anticipée (forward declaration) pour
TrafficLightcar les états y font référence.- Une méthode virtuelle pure
TrafficLight.h: Créez la classe de contexte qui maintient l'état actuel.Votre classe
TrafficLightdoit contenir unstd::unique_ptr<TrafficLightState>et commencer par défaut à l'état Red. Implémentez :setState(std::unique_ptr<TrafficLightState> newState)— change l'état actuelchange()— délègue à la méthode de changement de l'état actueldisplay()— affiche le statut du feu au format :[Color]: [Action]
main.cpp: Démontrez le cycle du feu de signalisation à travers les états.Lisez une entrée : le nombre de changements d'état à effectuer (entier).
Créez un
TrafficLightet affichez son état initial. Effectuez ensuite le nombre spécifié de changements, en affichant l'état après chaque changement.
Par exemple, avec l'entrée 3 :
Red: Stop
Green: Go
Yellow: Caution
Red: StopAvec l'entrée 6 :
Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: StopRemarquez comment le feu de signalisation cycle à travers ses états selon un schéma prévisible : Red → Green → Yellow → Red. Chaque état sait quel état vient ensuite et gère sa propre transition. La classe TrafficLight n'a besoin d'aucune logique conditionnelle pour déterminer ce qui se passe — elle délègue simplement à l'état dans lequel elle se trouve actuellement.
Aide-mémoire
Le patron d'état (State pattern) permet à un objet de modifier son comportement lorsque son état interne change. Au lieu d'utiliser des instructions conditionnelles complexes, chaque état est encapsulé dans une classe distincte.
Le patron se compose de trois composants :
- Interface d'état (State interface) : définit le comportement spécifique à l'état
- États concrets (Concrete States) : implémentent le comportement pour chaque état
- Contexte (Context) : maintient une référence vers l'état actuel
Structure de base :
#include <memory>
class Context; // Forward declaration
// State interface
class State {
public:
virtual void handleAction(Context& ctx) = 0;
virtual std::string getName() const = 0;
virtual ~State() = default;
};
// Context
class Context {
std::unique_ptr<State> state;
public:
void setState(std::unique_ptr<State> newState) {
state = std::move(newState);
}
void performAction() {
state->handleAction(*this);
}
std::string getStateName() const {
return state->getName();
}
};
// Concrete State
class ConcreteState : public State {
public:
void handleAction(Context& ctx) override {
// Handle action and transition to next state
ctx.setState(std::make_unique<AnotherState>());
}
std::string getName() const override {
return "ConcreteState";
}
};Chaque état gère les actions différemment et est responsable de la transition vers l'état suivant. Le Contexte délègue le comportement à l'état actuel sans avoir besoin de connaître la logique de transition.
Utilisez le patron d'état lorsqu'un comportement d'un objet dépend fortement de son état et que vous souhaitez éviter de grandes instructions conditionnelles.
Essayez vous-même
#include <iostream>
#include "TrafficLight.h"
using namespace std;
int main() {
// Lire le nombre de changements d'état
int numChanges;
cin >> numChanges;
// TODO: Créer un objet TrafficLight
// TODO: Afficher l'état initial
// TODO: Effectuer le nombre spécifié de changements
// Après chaque changement, afficher l'état actuel
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique