Skip to content
intermediatePhase 49 · Low-Level Design

State Pattern

Allow objects to change behavior when their internal state changes.

30m
0 problems
Topic Progress0%

State Interface

The State pattern allows an object to alter its behavior when its internal state changes.

Core Structure

┌──────────────────────────┐
│      Context             │
├──────────────────────────┤
│ - state: State           │
├──────────────────────────┤
│ + request()              │
│ + setState(State)        │
└──────────┬───────────────┘
           │ uses
           ▼
┌──────────────────────────┐
│    «interface»           │
│       State              │
├──────────────────────────┤
│ + handle(context)        │
└──────────┬───────────────┘
           │ implemented by
     ┌─────┴──────┐
     │            │
┌────┴─────┐ ┌───┴──────┐
│ StateA   │ │ StateB   │
└──────────┘ └──────────┘

State Interface

public interface State {
    void handle(VendingMachine machine);
    
    // Optional: available operations in this state
    default void insertCoin(VendingMachine machine) {
        throw new IllegalStateException("Cannot insert coin in this state");
    }
    
    default void selectItem(VendingMachine machine) {
        throw new IllegalStateException("Cannot select item in this state");
    }
    
    default void dispense(VendingMachine machine) {
        throw new IllegalStateException("Cannot dispense in this state");
    }
}

Context Class

public class VendingMachine {
    private State state;
    private int balance;
    private Item selectedItem;
    
    public VendingMachine() {
        this.state = new IdleState(); // Initial state
        this.balance = 0;
    }
    
    public void setState(State state) {
        this.state = state;
    }
    
    public void insertCoin(int amount) {
        state.insertCoin(this);
    }
    
    public void selectItem(String itemId) {
        state.selectItem(this);
    }
    
    public void dispense() {
        state.dispense(this);
    }
    
    // Getters
    public int getBalance() { return balance; }
    public void addBalance(int amount) { this.balance += amount; }
    public Item getSelectedItem() { return selectedItem; }
    public void setSelectedItem(Item item) { this.selectedItem = item; }
}

How It Works

IdleState.insertCoin(machine)
  → machine.addBalance(amount)
  → machine.setState(HasCoinState)

HasCoinState.selectItem(machine, itemId)
  → machine.setSelectedItem(item)
  → machine.setState(ItemSelectedState)

ItemSelectedState.dispense(machine)
  → dispense item
  → machine.setState(IdleState)

Concrete States

Each concrete state implements behavior specific to that state.

Idle State

public class IdleState implements State {
    @Override
    public void insertCoin(VendingMachine machine) {
        machine.addBalance(coin);
        System.out.println("Coin inserted: $" + coin);
        machine.setState(new HasCoinState());
    }
    
    @Override
    public void selectItem(VendingMachine machine) {
        System.out.println("Insert coin first");
    }
    
    @Override
    public void dispense(VendingMachine machine) {
        System.out.println("Insert coin and select item first");
    }
}

Has Coin State

public class HasCoinState implements State {
    @Override
    public void insertCoin(VendingMachine machine) {
        machine.addBalance(coin);
        System.out.println("Additional coin inserted");
    }
    
    @Override
    public void selectItem(VendingMachine machine, String itemId) {
        Item item = inventory.get(itemId);
        if (item == null) {
            System.out.println("Item not found");
            return;
        }
        if (machine.getBalance() < item.getPrice()) {
            System.out.println("Insufficient balance");
            return;
        }
        machine.setSelectedItem(item);
        machine.setState(new DispensingState());
    }
    
    @Override
    public void dispense(VendingMachine machine) {
        System.out.println("Select an item first");
    }
}

Dispensing State

public class DispensingState implements State {
    @Override
    public void dispense(VendingMachine machine) {
        Item item = machine.getSelectedItem();
        int change = machine.getBalance() - item.getPrice();
        
        // Dispense item
        inventory.remove(item.getId());
        System.out.println("Dispensing: " + item.getName());
        
        // Return change
        if (change > 0) {
            System.out.println("Change: $" + change);
        }
        
        // Reset and go to idle
        machine.setSelectedItem(null);
        machine.setState(new IdleState());
    }
}

State Diagram

┌──────────┐   insertCoin   ┌──────────┐
│   Idle   │───────────────▶│ HasCoin  │
└──────────┘                └────┬─────┘
                                 │
                            selectItem
                                 │
                                 ▼
┌──────────┐   dispense    ┌──────────┐
│   Idle   │◀───────────────│Dispensing│
└──────────┘                └──────────┘

Context Class

The Context class maintains the current state and delegates behavior to it.

Order Processing Example

public class OrderContext {
    private OrderState state;
    private Order order;
    
    public OrderContext(Order order) {
        this.order = order;
        this.state = new NewOrderState();
    }
    
    public void process() {
        state.process(this);
    }
    
    public void cancel() {
        state.cancel(this);
    }
    
    public void ship() {
        state.ship(this);
    }
    
    public void deliver() {
        state.deliver(this);
    }
    
    public void setState(OrderState state) {
        this.state = state;
        System.out.println("State changed to: " + state.getName());
    }
    
    public Order getOrder() { return order; }
}

// States
public interface OrderState {
    String getName();
    void process(OrderContext context);
    void cancel(OrderContext context);
    void ship(OrderContext context);
    void deliver(OrderContext context);
}

public class NewOrderState implements OrderState {
    public String getName() { return "NEW"; }
    
    public void process(OrderContext ctx) {
        // Process payment
        ctx.setState(new ProcessingState());
    }
    
    public void cancel(OrderContext ctx) {
        ctx.setState(new CancelledState());
    }
    
    public void ship(OrderContext ctx) {
        throw new IllegalStateException("Cannot ship new order");
    }
    
    public void deliver(OrderContext ctx) {
        throw new IllegalStateException("Cannot deliver new order");
    }
}

public class ProcessingState implements OrderState {
    public String getName() { return "PROCESSING"; }
    
    public void process(OrderContext ctx) {
        throw new IllegalStateException("Already processing");
    }
    
    public void cancel(OrderContext ctx) {
        ctx.setState(new CancelledState());
    }
    
    public void ship(OrderContext ctx) {
        ctx.setState(new ShippedState());
    }
    
    public void deliver(OrderContext ctx) {
        throw new IllegalStateException("Cannot deliver before shipping");
    }
}

public class ShippedState implements OrderState {
    public String getName() { return "SHIPPED"; }
    
    public void process(OrderContext ctx) {
        throw new IllegalStateException("Already shipped");
    }
    
    public void cancel(OrderContext ctx) {
        throw new IllegalStateException("Cannot cancel shipped order");
    }
    
    public void ship(OrderContext ctx) {
        throw new IllegalStateException("Already shipped");
    }
    
    public void deliver(OrderContext ctx) {
        ctx.setState(new DeliveredState());
    }
}

public class DeliveredState implements OrderState {
    public String getName() { return "DELIVERED"; }
    
    // All methods throw - terminal state
}

public class CancelledState implements OrderState {
    public String getName() { return "CANCELLED"; }
    
    // All methods throw - terminal state
}

State Transition Table

┌───────────┬─────────┬──────────┬────────┬─────────┐
│ Current   │ process │  cancel  │  ship  │ deliver │
├───────────┼─────────┼──────────┼────────┼─────────┤
│ NEW       │ → PROC  │ → CANCEL │ ERROR  │ ERROR   │
│ PROCESSING│ ERROR   │ → CANCEL │ → SHIP │ ERROR   │
│ SHIPPED   │ ERROR   │ ERROR    │ ERROR  │ → DELIV │
│ DELIVERED │ ERROR   │ ERROR    │ ERROR  │ ERROR   │
│ CANCELLED │ ERROR   │ ERROR    │ ERROR  │ ERROR   │
└───────────┴─────────┴──────────┴────────┴─────────┘

Benefits of State Pattern

  1. Eliminates if/else chains: No more switch(state) everywhere
  2. Single Responsibility: Each state class handles one state
  3. Open-Closed: Add new states without modifying existing
  4. Clear transitions: State transitions are explicit
  5. Encapsulated behavior: Each state encapsulates its behavior

Practice Problems

0/3solved
Design State Pattern System

Design a scalable State Pattern system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
State Pattern Scaling

How would you scale State Pattern to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
State Pattern Failure Modes

Analyze potential failure modes for State Pattern and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. What does the State pattern allow?

Question 1 options

2. What does the Context class do in the State pattern?

Question 2 options

3. What does the State pattern replace?

Question 3 options

4. What is a state transition?

Question 4 options

5. What principle does the State pattern follow?

Question 5 options

Flashcards

Question

What is the State pattern?

Answer

Allows an object to alter behavior when internal state changes. Each state is a class implementing the state interface.

Question

What is the Context in State pattern?

Answer

Maintains current state reference. Delegates behavior to current state. Provides setState() for state transitions.

Question

What does State pattern replace?

Answer

Complex if/else or switch chains for state-dependent behavior. Replaces with polymorphic state objects.

Question

State vs Strategy pattern?

Answer

State: behavior changes with internal state (automatic transitions). Strategy: algorithm selected externally (manual swapping).

Question

Real-world State pattern example?

Answer

Vending machine states (idle, has coin, dispensing), Order states (new, processing, shipped, delivered), TCP connection states.

Revision Notes

Key Takeaways

  • 1.State pattern allows objects to change behavior based on internal state
  • 2.Each state is a class implementing the state interface
  • 3.Context delegates behavior to current state and manages transitions
  • 4.Replaces complex conditional logic with polymorphic state objects
  • 5.State transitions are explicit and encapsulated in state classes

Interview Tips

  • Show State pattern for modeling order/game/workflow states
  • Explain how State eliminates complex conditional logic
  • Discuss state transitions and valid state changes
  • Compare State vs Strategy for algorithm selection

Cheat Sheet

State Pattern - Cheat Sheet

Purpose:
Object changes behavior when state changes.

Structure:

Context
├── state: State
├── setState(state)
└── request() → state.handle()

State (interface)
├── ConcreteStateA
└── ConcreteStateB

Key Points:

  • Each state class handles one state
  • State transitions via context.setState()
  • Context delegates to current state
  • Eliminates if/else chains

State vs Strategy:

State Strategy
Selection Internal (automatic) External (manual)
Purpose State-dependent Algorithm selection
Transitions Automatic Manual

Use Cases:

  • Vending machine
  • Order processing
  • TCP connections
  • Game states
  • Traffic lights