State: State Machine Pattern
The Problem It Solves
A vending machine’s response to “select item” depends on its mode: idle → ask for money; has-credit → dispense; dispensing → reject; out-of-stock → refund. The conditional version scatters if (state == ...) across every method — adding a state touches all of them, and illegal combinations (dispensing and collecting coins) compile happily. State pattern promotes each state to a class; the context delegates everything to its current state object, and behavior varies polymorphically.
CONDITIONALS STATE OBJECTS
select() { Context ──► State ◁─ IdleState
if (idle) {...} ◁─ HasCreditState
if (hasCredit) {...} becomes ◁─ DispensingState
if (dispensing){...} ◁─ OutOfStockState
} each state implements ALL events;
impossible events throw explicitly
Mechanics
interface VendingState {
void insertCoin(VendingMachine m, Money coin);
void selectItem(VendingMachine m, String code);
}
class IdleState implements VendingState {
public void insertCoin(VendingMachine m, Money c) {
m.addCredit(c);
m.setState(new HasCreditState()); // transition = swap the object
}
public void selectItem(VendingMachine m, String code) {
throw new IllegalStateException("Insert coins first");
}
}
class HasCreditState implements VendingState {
public void insertCoin(VendingMachine m, Money c) { m.addCredit(c); }
public void selectItem(VendingMachine m, String code) {
m.dispense(code);
m.setState(m.stock().isEmpty(code) ? new OutOfStockState() : new IdleState());
}
}
// CONTEXT — a thin delegator:
class VendingMachine {
private VendingState state = new IdleState();
void setState(VendingState s) { this.state = s; }
void insertCoin(Money c) { state.insertCoin(this, c); } // ← delegation only
void selectItem(String code) { state.selectItem(this, code); }
}
Illegal operations are explicit exceptions at the state that owns them — no silent no-ops.
Transitions: Who Owns Them?
| Owner | Effect |
|---|---|
| States decide next state (above) | Transition logic localized per state |
| Context holds a transition table | Central map: easier to audit whole machine |
| Rule | Either — never both; split ownership hides the lifecycle |
Enum States vs Class States
| Enum + switch in context | Class-per-state | |
|---|---|---|
| Boilerplate | Minimal | One class per state |
| Adding state | Edit one switch | New class, others untouched |
| State-specific fields/behavior | Awkward | Natural |
| Fits | Status tracking (order status) | Rich behavioral machines (games, protocols) |
Most business “state machines” are enum-shaped; reach for classes when states carry data or divergent behavior.
Real-World Sightings
- TCP connection states (protocol spec is literally this diagram).
- Media players (playing/paused/stopped), turn-based game engines.
- Workflow engines (BPM frameworks model entire processes as state machines).
Trade-offs & Pitfalls
- Class explosion for trivial machines — YAGNI applies; enum first.
- Shared mutable state passed via context needs care under concurrency — machines with concurrent triggers need event serialization or locking at context level.
- Forgetting terminal/error states leaves machines wedged; model them explicitly.
Interview Framing
- The vending machine / ATM question is canonical; producing interface + two states + explicit illegal-event throws closes the core loop.
- Naming when not to use it (“this is really just an order status — enum suffices”) demonstrates judgment interviewers reward.
Premium Content
Unlock State and all premium lessons with a subscription.
From ₹199.99/year — See plans