Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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 TrafficLightState avec :

    • 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 Green
    • YellowState — la couleur est "Yellow", l'action est "Caution", change vers Red
    • GreenState — la couleur est "Green", l'action est "Go", change vers Yellow

    Vous aurez besoin d'une déclaration anticipée (forward declaration) pour TrafficLight car les états y font référence.

  • TrafficLight.h : Créez la classe de contexte qui maintient l'état actuel.

    Votre classe TrafficLight doit contenir un std::unique_ptr<TrafficLightState> et commencer par défaut à l'état Red. Implémentez :

    • setState(std::unique_ptr<TrafficLightState> newState) — change l'état actuel
    • change() — délègue à la méthode de changement de l'état actuel
    • display() — 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 TrafficLight et 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: Stop

Avec l'entrée 6 :

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

Remarquez 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;
}
quiz iconTestez-vous

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