Before you start
State is one of the most under-taught patterns. Most bootcamps mention it once. Meanwhile, half the "if/elif on a state field" code in the industry could be the State pattern.
Learn it here; use it constantly.
The one-line version
State turns an object's mode-of-operation into its own class, with the behavior for that mode living there. The object holds a reference to its current state and delegates.
The signal to use State
- Multiple modes / phases (3+) with different behavior per mode
- Multiple actions (2+) that behave differently in different modes
- Explicit transition rules — only certain mode-changes are allowed
- State transitions triggered by actions — the object moves from A to B in response to some call
Any 2-3 of these → State.
Where State shows up in real design problems
Nearly EVERY design problem has a state machine somewhere:
- Vending Machine (this lesson)
- Elevator — moving up, moving down, stopped, doors-open
- TCP connection — LISTEN, SYN_SENT, ESTABLISHED, CLOSING
- Order lifecycle — PENDING, PAID, SHIPPED, DELIVERED, RETURNED, REFUNDED
- Media player — STOPPED, PLAYING, PAUSED
- UI wizard — step 1, step 2, step 3, complete, error
- Booking flow — search, hold, payment, confirmation
- Game character — idle, running, jumping, attacking, dead
- Chess piece (unusual states: en passant, castling eligibility)
- Traffic light — red, yellow, green
If you catch yourself writing an if self.status == "X": branch in more than one method — you've spotted a
State opportunity.
The State vs Strategy question
Both hold a "swappable behavior" object. The real difference:
- Strategy — the caller PICKS which strategy is active. It doesn't change unless the caller changes it.
- State — the object TRANSITIONS between states in response to actions. Each state knows about the next state.
Quick test: does the "which flavor?" decision come from outside (Strategy) or from the object itself (State)? Different patterns; often confused.
Where do transitions live?
Two schools:
- Inside the state class (this lesson's approach) — each state knows which state to transition to for each action. Pro: transitions live close to the behavior that causes them. Con: states know about other states.
- Outside — a TransitionManager decides based on the current state and action. Pro: transitions all in one place. Con: state classes get dumber; the manager gets smart in a way that recreates the if/elif smell.
For interviews, "inside the state class" is the standard answer. For very complex state machines (10+ states), a transition table can be cleaner.
The "invalid transition" question
Two schools:
- Reject silently (return a string / status) — our lesson. Not fatal; the caller can react.
- Raise an exception — invalid transitions are bugs. Fail loudly.
Both are valid. Depends on the problem. For a vending machine, quiet rejection with a user-visible message ("insert coin first") is right. For a TCP state machine, invalid transitions are protocol violations — raise.
The AST test
Our test AST-scans VendingMachine's methods for
isinstance or if statements. If any snuck in, the
pattern's payoff is gone. The whole point of State is
to move all the branching INTO the state classes — the
outer object should be a thin dispatcher.
The decision cheat-sheet
| Situation | Pattern |
|---|---|
| Object mode changes based on its actions | State |
| Algorithm chosen from outside | Strategy |
| Actions need undo | Command |
| Complex transitions with guards + actions on edges | Full state machine library |
| Only 2 states, 1 action | Just use a bool |
| Behavior depends on TWO state variables | Break into two state machines OR use a state table |