In one sentence: State lets an object alter its behavior when its internal state changes by giving each mode its own class — GoF's phrase is that the object will appear to change its class.
Real-world analogy
Your phone has one side button, and you press it the same way every time. When the phone is asleep, the press wakes the screen. When it's awake in your hand, the same press puts it to sleep. When it's ringing, that press silences the ring and lets the call go on buzzing quietly. One button, one gesture, three unrelated outcomes.
Nothing in your thumb decides which of those happens. The phone does, and it decides by consulting what it currently is. Each mode owns its own answer to "side button pressed" — and each mode also determines what the phone becomes next, which is why the press that wakes it leaves you somewhere different from the press that silenced a call.
Hand the phone to someone who has only ever seen it asleep and they will describe a device that turns on when you press the side. Hand it to them mid-call and they'll describe a mute switch. Same hardware, same button: it behaves like a different device depending on the mode it's in.
Mapping the analogy to the pattern:
- The phone → the Context: the only thing your thumb ever touches, and the only thing a client deals with.
- Each mode — asleep, awake, ringing → a ConcreteState, holding that mode's reactions and nothing else.
- Pressing the side button → the single Context operation that delegates the event straight to whichever state is current.
- The mode the press leaves you in → a state naming its own successor, which is what makes transitions explicit instead of anonymous.
- "It behaves like a different device" → GoF's phrase exactly: the object appears to change its class.
Another angle: a traffic light on the same tick of the clock. Green becomes amber, amber becomes red, red becomes green. The trigger is identical every time; the colour currently lit decides both the response and what comes after it.
The problem
GoF's example is a TCP connection, which answers the same Open request completely differently depending on whether it is Closed, Listening, or Established. Ours is a music player with three modes: ready, playing, and locked. Written the obvious way, every button becomes a conditional:
- Conditionals multiply.
pressPlay()needs a branch per mode, and so doespressLock(), and so does every button you add later. - The rules are scattered. What "locked" means is spread across five methods, so adding a fourth mode means finding and editing all of them.
- Transitions get lost. Assignments like
mode = 2;sit buried inside branches, and nothing documents which jumps are legal.
The solution
Turn each mode into a class:
- Define a state interface with one method per event the object handles.
- Write one class per state, containing only that state's reactions.
- The context object keeps a pointer to its current state and delegates every event to it.
- A state decides what comes next by handing back its successor.
Each state class is small and self-contained, and the legal transitions are visible in the code that returns them.
Note: Step 4 is a choice, not a rule. GoF is explicit that the pattern does not say who defines the transitions. If the criteria are fixed, put them entirely in the context. Letting the state subclasses name their own successor — what the example below does — is usually more flexible and makes the logic easy to extend by adding a class, but it costs you something: each state now knows about at least one other, so the subclasses acquire dependencies on each other. Keeping the decision in the context avoids that at the price of a central place that must be edited for every new state.
Structure
Three participants, and the context is the only one clients see:
- Context — defines the interface clients actually call, and holds an instance of a ConcreteState subclass representing the current state.
Playerhere; GoF's isTCPConnection. Clients configure a context with a state and then never deal with State objects directly. - State — the interface that encapsulates behavior associated with one state of the context.
Statehere, withplay()andlock()— one method per event. - ConcreteState — one subclass per state, each implementing the behavior for that state.
Ready,Playing, andLocked; GoF's areTCPEstablished,TCPListen, andTCPClosed.
A context often passes itself to the state object handling the request, so the state can read the context's data and set its successor. The example takes the simpler route of returning the next state instead.
C++ example
An audio player. Ready, Playing, and Locked each answer the two buttons in their own way; a state returns the next state, or nullptr to stay put.
#include <iostream>
#include <memory>
#include <string>
class State {
public:
virtual ~State() = default;
virtual std::unique_ptr<State> play() = 0; // returns next state, or nullptr to stay
virtual std::unique_ptr<State> lock() = 0;
virtual std::string name() const = 0;
};
class Player {
public:
explicit Player(std::unique_ptr<State> start) : state_(std::move(start)) {}
void pressPlay() { apply(state_->play()); }
void pressLock() { apply(state_->lock()); }
private:
void apply(std::unique_ptr<State> next) {
if (!next) return;
std::cout << " [" << state_->name() << " -> " << next->name() << "]\n";
state_ = std::move(next);
}
std::unique_ptr<State> state_;
};
class Locked : public State {
public:
std::unique_ptr<State> play() override {
std::cout << "Play ignored: the player is locked\n";
return nullptr;
}
std::unique_ptr<State> lock() override; // -> Ready
std::string name() const override { return "Locked"; }
};
class Playing : public State {
public:
std::unique_ptr<State> play() override; // -> Ready
std::unique_ptr<State> lock() override {
std::cout << "Locking; music keeps playing\n";
return std::make_unique<Locked>();
}
std::string name() const override { return "Playing"; }
};
class Ready : public State {
public:
std::unique_ptr<State> play() override {
std::cout << "Starting playback\n";
return std::make_unique<Playing>();
}
std::unique_ptr<State> lock() override {
std::cout << "Locking\n";
return std::make_unique<Locked>();
}
std::string name() const override { return "Ready"; }
};
std::unique_ptr<State> Locked::lock() {
std::cout << "Unlocking\n";
return std::make_unique<Ready>();
}
std::unique_ptr<State> Playing::play() {
std::cout << "Pausing\n";
return std::make_unique<Ready>();
}
int main() {
Player player(std::make_unique<Ready>());
player.pressPlay();
player.pressLock();
player.pressPlay(); // rejected by Locked
player.pressLock();
player.pressPlay();
}Starting playback
[Ready -> Playing]
Locking; music keeps playing
[Playing -> Locked]
Play ignored: the player is locked
Unlocking
[Locked -> Ready]
Starting playback
[Ready -> Playing]The same pressPlay() call starts playback, pauses it, and is refused outright — the button didn't change, the object behind it did.
When should you use it?
GoF lists two cases, and either one on its own is enough. Use State when:
- An object's behavior depends on its state, and it must change that behavior at runtime as the state changes.
- Operations contain large, multipart conditionals that all branch on the same state, usually held in one or more enumerated constants, and the same conditional shape is repeated across several methods. State puts each branch of that conditional in its own class, which promotes the state from a value to an object that can vary independently.
Pros and cons
Pros (GoF's consequences)
- It localizes state-specific behavior and partitions behavior by state. Everything about being "locked" lives in one class, so new states and transitions arrive as new subclasses rather than edits scattered across every method.
- It makes transitions explicit. When the state is just a data value, transitions have no representation at all — they are anonymous assignments to a variable. Separate state objects turn them into something you can point at. They are also atomic from the context's point of view: one variable is rebound, not several, so the context can't be caught halfway between states.
- State objects can be shared. If a state has no instance variables — if the state it represents is encoded entirely in its type — every context can share a single instance. Shared this way they are essentially flyweights: pure behavior, no intrinsic state.
Cons
- More classes, less compact. GoF concedes the pattern distributes behavior across several subclasses, which is worse than one class if you only have two trivial states — and better as soon as you have many, since the alternative is large conditionals.
- The overall state machine is spread across files; a diagram or comment helps.
- Allocating a new state object on each transition costs more than flipping an enum.
Tip: GoF frames the allocation question as a real trade-off. Create state objects on demand when you don't know in advance which states will be entered and transitions are rare; create them once up front and never destroy them when transitions are frequent, so you pay instantiation cost once. Since
Ready,Playing, andLockedcarry no data, the second approach applies here — return a shared static instance instead of allocating a fresh one on every transition.
How to remember it
Memory hook: "The object swaps its own brain." Same button, different behavior, because the thing behind the button replaced itself. The confusable sibling is Strategy, which has an identical class diagram: the difference is who decides. With Strategy the client picks the algorithm; with State the object changes its own behavior, either from inside its state objects or from the context, and the client just keeps pressing the same button.
Related patterns
- Flyweight — GoF points here for when and how State objects can be shared: a state with no instance variables is a flyweight with behavior and no intrinsic state.
- Singleton — GoF notes state objects are often Singletons; in the book's TCP example each
TCPStatesubclass hands out its one instance through a staticInstance(). - Strategy — the same structure aimed at a different problem: interchangeable algorithms chosen from outside, rather than modes an object moves between.