BehavioralC++verifiedVerified
State Pattern in C++
Allows an object to alter its behavior when its internal state changes, appearing as if the object has changed its class.
How to Implement the State Pattern in C++
1Step 1: Define the State interface
class State {
public:
virtual ~State() = default;
virtual std::string name() const = 0;
virtual std::unique_ptr<State> next() const = 0;
virtual std::string action() const = 0;
};2Step 2: Concrete states
class GreenState : public State {
public:
std::string name() const override { return "GREEN"; }
std::unique_ptr<State> next() const override;
std::string action() const override { return "Cars may proceed"; }
};
class YellowState : public State {
public:
std::string name() const override { return "YELLOW"; }
std::unique_ptr<State> next() const override;
std::string action() const override { return "Cars should slow down"; }
};
class RedState : public State {
public:
std::string name() const override { return "RED"; }
std::unique_ptr<State> next() const override {
return std::make_unique<GreenState>();
}
std::string action() const override { return "Cars must stop"; }
};
std::unique_ptr<State> GreenState::next() const {
return std::make_unique<YellowState>();
}
std::unique_ptr<State> YellowState::next() const {
return std::make_unique<RedState>();
}3Step 3: Context that delegates to the current state
class TrafficLight {
std::unique_ptr<State> state_;
public:
TrafficLight() : state_(std::make_unique<RedState>()) {}
void transition() { state_ = state_->next(); }
void display() const {
std::cout << "[" << state_->name() << "] " << state_->action() << "\n";
}
};
int main() {
TrafficLight light;
for (int i = 0; i < 6; ++i) {
light.display();
light.transition();
}
}#include <iostream>
#include <string>
#include <memory>
#include <map>
#include <functional>
#include <format>
#include <vector>
#include <stdexcept>
// [step] Define the state machine types
class StateMachine;
struct Transition {
std::string fromState;
std::string event;
std::string toState;
std::function<bool()> guard; // optional condition
std::function<void()> action; // side effect
};
class IState {
public:
virtual ~IState() = default;
virtual std::string name() const = 0;
virtual void onEnter() {}
virtual void onExit() {}
};
using StateFactory = std::function<std::unique_ptr<IState>()>;
// [step] Build the state machine with guards, actions, and logging
class StateMachine {
std::map<std::string, StateFactory> stateFactories_;
std::vector<Transition> transitions_;
std::unique_ptr<IState> current_;
std::vector<std::string> log_;
public:
void registerState(const std::string& name, StateFactory factory) {
stateFactories_[name] = std::move(factory);
}
void addTransition(Transition t) {
transitions_.push_back(std::move(t));
}
void start(const std::string& initialState) {
current_ = createState(initialState);
current_->onEnter();
log_.push_back(std::format("Started in '{}'", initialState));
}
bool trigger(const std::string& event) {
for (const auto& t : transitions_) {
if (t.fromState == current_->name() && t.event == event) {
if (t.guard && !t.guard()) {
log_.push_back(std::format("Guard blocked '{}' in '{}'",
event, current_->name()));
return false;
}
current_->onExit();
if (t.action) t.action();
log_.push_back(std::format("'{}' -- {} --> '{}'",
t.fromState, event, t.toState));
current_ = createState(t.toState);
current_->onEnter();
return true;
}
}
log_.push_back(std::format("No transition for '{}' in '{}'",
event, current_->name()));
return false;
}
const std::string& currentState() const { return current_->name(); }
const std::vector<std::string>& history() const { return log_; }
private:
std::unique_ptr<IState> createState(const std::string& name) {
auto it = stateFactories_.find(name);
if (it == stateFactories_.end())
throw std::runtime_error(std::format("Unknown state: '{}'", name));
return it->second();
}
};
// [step] Example: order processing state machine
class DraftState : public IState {
public:
std::string name() const override { return "draft"; }
void onEnter() override { std::cout << "Entering draft state\n"; }
};
class SubmittedState : public IState {
public:
std::string name() const override { return "submitted"; }
void onEnter() override { std::cout << "Order submitted for review\n"; }
};
class ApprovedState : public IState {
public:
std::string name() const override { return "approved"; }
void onEnter() override { std::cout << "Order approved\n"; }
};
class ShippedState : public IState {
public:
std::string name() const override { return "shipped"; }
void onEnter() override { std::cout << "Order shipped\n"; }
};
class CancelledState : public IState {
public:
std::string name() const override { return "cancelled"; }
void onEnter() override { std::cout << "Order cancelled\n"; }
};
int main() {
StateMachine sm;
sm.registerState("draft", [] { return std::make_unique<DraftState>(); });
sm.registerState("submitted", [] { return std::make_unique<SubmittedState>(); });
sm.registerState("approved", [] { return std::make_unique<ApprovedState>(); });
sm.registerState("shipped", [] { return std::make_unique<ShippedState>(); });
sm.registerState("cancelled", [] { return std::make_unique<CancelledState>(); });
sm.addTransition({"draft", "submit", "submitted", nullptr, nullptr});
sm.addTransition({"submitted", "approve", "approved", nullptr, nullptr});
sm.addTransition({"approved", "ship", "shipped", nullptr, nullptr});
sm.addTransition({"draft", "cancel", "cancelled", nullptr, nullptr});
sm.addTransition({"submitted", "cancel", "cancelled", nullptr, nullptr});
sm.start("draft");
sm.trigger("submit");
sm.trigger("approve");
sm.trigger("ship");
std::cout << std::format("\nFinal state: {}\n", sm.currentState());
std::cout << "History:\n";
for (const auto& entry : sm.history())
std::cout << " " << entry << "\n";
}State Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
State Pattern in the Real World
“Think of a traffic light. The light itself (context) doesn't change its wiring, but its active state—red, yellow, or green—completely determines what drivers should do. Each color has its own rules, and the light transitions through states on a timer. Adding a flashing-yellow state only requires defining that state's rules, not rewiring the entire light.”