🧾 State Design Pattern – LLD + Java + Real-World Use Case



This content originally appeared on DEV Community and was authored by DevCorner2

Allow an object to alter its behavior when its internal state changes β€” making the object appear to change its class.

🧠 What Is the State Pattern?

The State Pattern is a behavioral design pattern that:

  • Encapsulates state-specific behavior into separate classes.
  • Delegates state transitions and behavior to these state objects.
  • Avoids conditionals (if-else, switch-case) spread across methods.

βœ… Real-World Use Cases

System/Domain Example of States
🎫 Movie Booking SeatAvailable β†’ Reserved β†’ Paid β†’ Cancelled
🏧 ATM Machine CardInserted, PinVerified, CashDispensed
🚦 Traffic Signal Red β†’ Yellow β†’ Green
πŸ“Ά Network Connection Connected, Disconnected, Reconnecting
πŸ“„ Document Workflow Draft, Reviewed, Approved, Published

πŸ“Œ When to Use It?

βœ… Use State Pattern when:

  • An object must behave differently depending on internal state.
  • You have lots of conditional logic (if/switch) that varies by state.
  • State transitions are part of business logic.

🧱 Pattern Structure (LLD)

Component Responsibility
State (interface) Declares operations per state
ConcreteState Implements behavior for a specific state
Context Maintains current state and delegates behavior

πŸ“ UML Diagram

+---------------------+
|       Context       |
+---------------------+
| - state: State      |
| +setState(State)    |
| +handle()           |
+---------------------+
         β–²
         |
+---------------------+       +------------------------+
|       State         |<------+   ConcreteStateX       |
+---------------------+       +------------------------+
| +handle(Context)    |       | +handle(Context)       |
+---------------------+       +------------------------+

πŸ’» Java Implementation – Vending Machine Example

We’ll model a vending machine with 3 states:

  • IdleState: Waiting for coin
  • HasCoinState: Coin inserted, ready to select item
  • DispensingState: Dispensing item

βœ… VendingState.java

public interface VendingState {
    void insertCoin(VendingMachine context);
    void selectItem(VendingMachine context);
    void dispense(VendingMachine context);
}

βœ… IdleState.java

public class IdleState implements VendingState {
    @Override
    public void insertCoin(VendingMachine context) {
        System.out.println("🪙 Coin inserted. Ready to select item.");
        context.setState(new HasCoinState());
    }

    @Override
    public void selectItem(VendingMachine context) {
        System.out.println("❌ Insert coin first.");
    }

    @Override
    public void dispense(VendingMachine context) {
        System.out.println("❌ Cannot dispense. No item selected.");
    }
}

βœ… HasCoinState.java

public class HasCoinState implements VendingState {
    @Override
    public void insertCoin(VendingMachine context) {
        System.out.println("❌ Coin already inserted.");
    }

    @Override
    public void selectItem(VendingMachine context) {
        System.out.println("📦 Item selected. Dispensing now...");
        context.setState(new DispensingState());
    }

    @Override
    public void dispense(VendingMachine context) {
        System.out.println("❌ Select an item first.");
    }
}

βœ… DispensingState.java

public class DispensingState implements VendingState {
    @Override
    public void insertCoin(VendingMachine context) {
        System.out.println("❌ Please wait. Dispensing in progress.");
    }

    @Override
    public void selectItem(VendingMachine context) {
        System.out.println("❌ Already dispensing.");
    }

    @Override
    public void dispense(VendingMachine context) {
        System.out.println("✅ Item dispensed. Back to idle.");
        context.setState(new IdleState());
    }
}

βœ… VendingMachine.java (Context)

public class VendingMachine {
    private VendingState currentState;

    public VendingMachine() {
        this.currentState = new IdleState(); // Initial state
    }

    public void setState(VendingState state) {
        this.currentState = state;
    }

    public void insertCoin() {
        currentState.insertCoin(this);
    }

    public void selectItem() {
        currentState.selectItem(this);
    }

    public void dispense() {
        currentState.dispense(this);
    }
}

βœ… Client.java

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

        machine.selectItem();    // ❌ Insert coin first.
        machine.insertCoin();    // 🪙 Coin inserted.
        machine.insertCoin();    // ❌ Already inserted
        machine.selectItem();    // 📦 Dispensing
        machine.dispense();      // ✅ Dispensed, back to Idle
    }
}

πŸ§ͺ Output

❌ Insert coin first.
🪙 Coin inserted. Ready to select item.
❌ Coin already inserted.
📦 Item selected. Dispensing now...
✅ Item dispensed. Back to idle.

✨ Benefits

Benefit Description
βœ… Open/Closed Easily add new states without touching context
βœ… Clean code No complex if-else for state logic
βœ… Maintainability Each state is isolated, testable, reusable
βœ… Realistic modeling Closer to real-world workflows

⚠ Limitations

  • ❌ Number of classes can grow with complex state machines
  • ⚠ Context object may need to expose internals (setState)
  • πŸ”„ May be overkill for simple linear flows

🧾 Summary

Aspect Value
Pattern Type Behavioral
Intent Change behavior by state
Java Features Used Interfaces, Polymorphism, Delegation
Ideal for Workflow engines, UI flows, devices, FSMs

🎯 Interview Tip

If asked to build a Ticket Booking System, Document Approval Workflow, or Game Character State, the State Pattern is often the cleanest and most extensible design choice.


This content originally appeared on DEV Community and was authored by DevCorner2