Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

State
LLD

State

Understand how an object's behavior can change when its internal state changes.

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?

OwnerEffect
States decide next state (above)Transition logic localized per state
Context holds a transition tableCentral map: easier to audit whole machine
RuleEither — never both; split ownership hides the lifecycle

Enum States vs Class States

Enum + switch in contextClass-per-state
BoilerplateMinimalOne class per state
Adding stateEdit one switchNew class, others untouched
State-specific fields/behaviorAwkwardNatural
FitsStatus 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.

My Private Notes

Notes are auto-saved locally to this device.