Design Pattern

State Pattern

Clean Java-only production-ready implementation.


What Problem It Solves

An object changes its behavior when its internal state changes. Without State, you get giant if-else chains checking state in every method. With State, each state is a class with its own behavior. Adding a new state means one new class, not modifying every method.

The Problem (Without State)

class VendingMachine {
    String state = "IDLE";

    void insertCoin() {
        if (state.equals("IDLE")) { state = "HAS_MONEY"; }
        else if (state.equals("HAS_MONEY")) { /* already has money */ }
        else if (state.equals("DISPENSING")) { /* wait */ }
    }

    void selectItem(String item) {
        if (state.equals("IDLE")) { /* no money */ }
        else if (state.equals("HAS_MONEY")) { state = "DISPENSING"; }
        else if (state.equals("DISPENSING")) { /* already dispensing */ }
    }

    // Every method needs the same if-else chain.
    // Adding "MAINTENANCE" state means editing every method.
}

State vs Strategy

AspectStateStrategy
PurposeObject behaves differently based on its internal stateClient picks an algorithm
Who changesContext transitions automaticallyClient chooses explicitly
States shareThey know about each other (transitions)They're independent
When to useFinite state machine (order, vending machine, elevator)Plugin algorithms (pricing, split, ranking)

Common Interview Traps

TrapWhyFix
Giant if/else or switch on stateHard to extend, easy to miss a transitionExtract each state into its own class
Forgetting to define all transitionsSome states have invalid transitions that silently do nothingEvery method in every state should handle its transition explicitly (even if it's a no-op)
State classes holding context referencesCircular dependencyPass context as method parameter, not constructor dependency
Too few states"IDLE" and "BUSY" aren't enough for complex workflowsList all meaningful states from requirements

Where State Appears in LLD Problems

ProblemContextStates
Vending Machine (#7)VendingMachineIdle, HasMoney, Dispensing, SoldOut
Elevator (#3)ElevatorIdle, Moving, DoorOpen, Overloaded, Maintenance
Order Management (#68)OrderCreated, Paid, Processing, Shipped, Delivered, Cancelled
ATM (#13)ATMSessionIdle, CardInserted, PinVerified, Transaction, Ejecting
Parking Lot (#1)EntryGateAvailable, Occupied, Reserved, OutOfService
Chess (#14)GameWhiteTurn, BlackTurn, Check, Checkmate, Draw

Implementation

// โ”€โ”€โ”€ EXAMPLE 1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// WHAT WE ARE IMPLEMENTING:
// A vending machine controlling item selection and coin insertion
// transitions.
//
// WHERE THE STATE FITS IN:
// VendingMachineState is the State interface. HasCoinState and NoCoinState
// represent Concrete States. VendingMachine acts as the Context tracking
// states.
// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// --- State interface ---
interface VendingMachineState {
    void insertCoin(VendingMachine vm);
    void selectItem(VendingMachine vm, String item);
    void dispense(VendingMachine vm);
}

// --- Context ---
class VendingMachine {
    private VendingMachineState state;
    private int balance = 0;

    public VendingMachine() {
        state = new IdleState();
    }

    void setState(VendingMachineState state) {
        System.out.println("  [State] " + this.state.getClass().getSimpleName() + " -> " + state.getClass().getSimpleName());
        this.state = state;
    }
    void setBalance(int b) { this.balance = b; }
    int getBalance() { return balance; }

    public void insertCoin() { state.insertCoin(this); }
    public void selectItem(String item) { state.selectItem(this, item); }
    public void dispense() { state.dispense(this); }
}

// --- Concrete states ---
class IdleState implements VendingMachineState {
    public void insertCoin(VendingMachine vm) {
        System.out.println("  Coin inserted: $5");
        vm.setBalance(5);
        vm.setState(new HasMoneyState());
    }
    public void selectItem(VendingMachine vm, String item) {
        System.out.println("  Insert coin first");
    }
    public void dispense(VendingMachine vm) {
        System.out.println("  Insert coin first");
    }
}

class HasMoneyState implements VendingMachineState {
    public void insertCoin(VendingMachine vm) {
        System.out.println("  Coin already inserted. Balance: $" + vm.getBalance());
    }
    public void selectItem(VendingMachine vm, String item) {
        System.out.println("  Item selected: " + item);
        if (vm.getBalance() >= 5) {
            vm.setState(new DispensingState());
        } else {
            System.out.println("  Insufficient balance");
        }
    }
    public void dispense(VendingMachine vm) {
        System.out.println("  Select item first");
    }
}

class DispensingState implements VendingMachineState {
    public void insertCoin(VendingMachine vm) {
        System.out.println("  Dispensing in progress");
    }
    public void selectItem(VendingMachine vm, String item) {
        System.out.println("  Dispensing in progress");
    }
    public void dispense(VendingMachine vm) {
        System.out.println("  Dispensing item... Enjoy!");
        vm.setBalance(0);
        vm.setState(new IdleState());
    }
}

public class Main {
    public static void main(String[] args) {
        VendingMachine vm = new VendingMachine();

        // Full flow
        vm.insertCoin();        // Idle -> HasMoney
        vm.selectItem("Coke");  // HasMoney -> Dispensing
        vm.dispense();          // Dispensing -> Idle

        // Edge cases
        vm.dispense();          // Idle -> "Insert coin first"
        vm.selectItem("Chips"); // Idle -> "Insert coin first"
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...