State Pattern

Intermediate
Behavioral Patterns

Allows an object to alter its behavior when its internal state changes, appearing to change its class.

Overview

The State pattern models a Finite State Machine (FSM) in OOP. Instead of a massive switch/if-else on a status enum, each state is its own class implementing a State interface. The Context delegates behavior to its current State object. When a transition occurs, the Context replaces the current state with the new one. This eliminates the "illegal operation in state X" defensive checks scattered throughout code — invalid operations simply do nothing or throw in the wrong state class. Classic examples: traffic light, vending machine, order lifecycle (Pending → Confirmed → Shipped → Delivered), TCP connection states.

Vending Machine State Machine

A vending machine has four states: Idle, HasMoney, Dispensing, OutOfStock. Each state handles insertCoin(), selectProduct(), dispense(), and refund() differently. Invalid operations in a state throw or silently ignore.

Java — Vending Machine with State pattern
// State interface
public interface VendingMachineState {
    void insertCoin(VendingMachine machine, double amount);
    void selectProduct(VendingMachine machine, String product);
    void dispense(VendingMachine machine);
    void refund(VendingMachine machine);
}

// Context
public class VendingMachine {
    private VendingMachineState state;
    private double balance    = 0;
    private int    stockCount = 10;

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

    public void setState(VendingMachineState state) { this.state = state; }
    public double getBalance()                      { return balance; }
    public void setBalance(double b)                { this.balance = b; }
    public int  getStock()                          { return stockCount; }
    public void decrementStock()                    { stockCount--; }

    // Delegates all behavior to current state
    public void insertCoin(double amount)     { state.insertCoin(this, amount); }
    public void selectProduct(String product) { state.selectProduct(this, product); }
    public void dispense()                    { state.dispense(this); }
    public void refund()                      { state.refund(this); }
}

// Concrete States
public class IdleState implements VendingMachineState {
    @Override
    public void insertCoin(VendingMachine m, double amount) {
        m.setBalance(m.getBalance() + amount);
        System.out.println("Coin inserted: ₹" + amount + ". Balance: ₹" + m.getBalance());
        m.setState(new HasMoneyState()); // transition
    }
    @Override public void selectProduct(VendingMachine m, String p) { System.out.println("Please insert coin first"); }
    @Override public void dispense(VendingMachine m)                { System.out.println("Please insert coin first"); }
    @Override public void refund(VendingMachine m)                  { System.out.println("No money to refund"); }
}

public class HasMoneyState implements VendingMachineState {
    private String selectedProduct;

    @Override
    public void insertCoin(VendingMachine m, double amount) {
        m.setBalance(m.getBalance() + amount);
        System.out.println("Added ₹" + amount + ". Total: ₹" + m.getBalance());
    }

    @Override
    public void selectProduct(VendingMachine m, String product) {
        double price = 20.0; // simplified
        if (m.getBalance() >= price) {
            this.selectedProduct = product;
            System.out.println("Selected: " + product);
            m.setState(new DispensingState(product, price));
        } else {
            System.out.println("Insufficient balance. Need ₹" + price);
        }
    }

    @Override public void dispense(VendingMachine m)     { System.out.println("Please select a product first"); }

    @Override
    public void refund(VendingMachine m) {
        System.out.println("Refunding ₹" + m.getBalance());
        m.setBalance(0);
        m.setState(new IdleState());
    }
}

public class DispensingState implements VendingMachineState {
    private final String product;
    private final double price;

    public DispensingState(String product, double price) {
        this.product = product; this.price = price;
    }

    @Override
    public void dispense(VendingMachine m) {
        System.out.println("Dispensing: " + product);
        m.setBalance(m.getBalance() - price);
        m.decrementStock();
        if (m.getBalance() > 0) System.out.println("Change: ₹" + m.getBalance());
        m.setBalance(0);
        m.setState(m.getStock() > 0 ? new IdleState() : new OutOfStockState());
    }

    @Override public void insertCoin(VendingMachine m, double a) { System.out.println("Dispensing in progress"); }
    @Override public void selectProduct(VendingMachine m, String p) { System.out.println("Dispensing in progress"); }
    @Override public void refund(VendingMachine m)               { System.out.println("Cannot refund while dispensing"); }
}

public class OutOfStockState implements VendingMachineState {
    @Override public void insertCoin(VendingMachine m, double a)    { System.out.println("Out of stock — coin returned"); }
    @Override public void selectProduct(VendingMachine m, String p) { System.out.println("Out of stock"); }
    @Override public void dispense(VendingMachine m)                { System.out.println("Out of stock"); }
    @Override public void refund(VendingMachine m)                  { System.out.println("No money inserted"); }
}

// Usage
VendingMachine vm = new VendingMachine();
vm.insertCoin(20.0);
vm.selectProduct("Water");
vm.dispense();

Key Points to Remember

  • 1State eliminates if-else/switch chains on status fields — each state is a class.
  • 2Context delegates all operations to the current State; State transitions by replacing the state object.
  • 3State classes can reference the Context to trigger transitions (state.dispense() calls machine.setState(...)).
  • 4Invalid operations in a state are handled locally (throw or silently ignore) — no scattered null checks.
  • 5Order lifecycle (PENDING→CONFIRMED→SHIPPED→DELIVERED) and TCP connection are real-world State machines.

Interview Questions

Sign in to ask Aria
1

What is the difference between State and Strategy patterns?

MediumAmazon
2

How does State pattern eliminate if-else chains?

EasyGoogle
3

Design a traffic light system using the State pattern.

MediumUber
4

Who should be responsible for state transitions — Context or State?

HardMicrosoft
5

How would you persist and restore a State machine across application restarts?

HardNetflix

Ask Aria about State Pattern

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…