State
Let an object change its behavior when its internal state changes, so completely that it appears to have changed its class.
The problem
State is the object-oriented face of the finite-state machine: at any moment the program sits in one of a limited set of states, behaves differently in each, and moves between them along a fixed set of transitions.
Take a Document class with states Draft, Moderation, and Published.
Its publish() method sends a draft to moderation, publishes a moderated
document if the current user is an administrator, and does nothing at all once
published. The obvious implementation is a switch on a status field, and the
obvious implementation of the next method is the same switch again.
Now add a state. You are editing every method in the class, and each edit risks the others. Nobody predicts the full state graph up front, so the lean little conditional you wrote in month one grows into the thing you dread opening in month twelve.
The solution
Give every state its own class and move the state-specific behavior into it. The original object, now called the context, keeps one reference to a state object and delegates all state-dependent work to it. Its own methods shrink to a single forwarding line each.
Switching states means swapping that reference. This works only because every
state class implements the same interface, so the context never needs to know
which one it is holding. And because states usually carry a backreference to
the context, they can perform the swap themselves: ReadyState.clickPlay()
starts playback and installs a PlayingState.
That last detail is what separates State from Strategy. Strategies are interchangeable strangers picked by a client. States are a cast of characters who know the plot and hand off to each other.
- 1A button is pressed. The player has no idea what should happen, and that is by design.
- 2The context forwards the call to whatever state object it currently holds. No switch, no if, just delegation.
- 3ReadyState calls back into the context for the actual service work. States decide what happens; the context still owns the machinery.
- 4Then the state performs its own transition. This is the move Strategy never makes.
- 5The same button as before, pressed again.
- 6Different object answers now, so next means fast-forward five seconds instead of skipping a track. The player did not change one line of code.
- 7The user pockets the phone and locks it.
- 8Every subsequent button press lands in LockedState, whose methods politely do nothing at all.
Structure
The state interface should declare only the methods that genuinely vary by state. Padding it with methods that most states must implement as no-ops is a sign the seam is in the wrong place. When several states share behavior, put an intermediate abstract class between them rather than copy-pasting.
Code
Same example three ways: an audio player whose four buttons mean different things depending on whether it is locked, ready, or playing.
When to use it
- An object behaves differently depending on its current state, there are many states, and the state-specific code changes often.
- A class is drowning in conditionals that switch on the values of its own fields. Extracting each branch into a state class also lets you evict the temporary fields and helpers that only mattered to one branch.
- A conditional state machine has grown duplicate code across similar states, which an abstract base state can absorb.
Pitfalls
- Overkill on small machines. Three states that never change do not justify an interface and three classes. Wait for the churn.
- Transition logic scattered everywhere. If states, the context, and the client all install new states, nobody can read the state graph. Pick one owner per transition and be consistent.
- Coupling to concrete states. Whoever writes
new PlayingState(...)depends on that class. That is often fine, but if it spreads, funnel creation through the context or a small factory. - Fat state interfaces. Every state paying the tax of a method only one state uses means the interface is describing the union of behaviors rather than the shared contract.
Don't confuse it with
- Strategy. Structurally near-identical: a context delegating to a swappable helper. The intent differs. Strategy offers interchangeable algorithms for the same task, chosen by the client, mutually ignorant. State models a lifecycle whose members know each other and drive the transitions. State is fairly described as Strategy with the restriction lifted.
- Template Method. Template Method varies steps of a fixed algorithm through subclassing, decided at compile time and frozen per class. State varies whole behavior sets through composition, decided and re-decided at runtime.
- Bridge. Same composition skeleton again. Bridge exists so an abstraction and its implementation can evolve on separate axes; State exists so an object can act like a different class from one minute to the next.
Check yourself
What is the single sharpest difference between State and Strategy?