Low Level Design

State Pattern — Object Behaviour That Changes with State

How the State pattern replaces complex state-based conditionals with dedicated state classes, making transitions explicit and individual states independently testable.

August 26, 2026·9 min read

What is the State Pattern?#

The State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. From the outside, it appears as if the object changed its class.

Instead of giant if-else or switch blocks based on current state, each state becomes its own class that handles behavior for that specific state.

You reach for State when:

  • An object's behavior depends on its current state and must change at runtime
  • You have large conditionals (if-else / switch) that depend on an object's state
  • State-specific behavior needs to be easily extensible

Real-World Analogy#

Think of a traffic signal. It always responds to the same operation (change), but behaves very differently depending on its current state:

  • 🟢 Green → cars pass; transitions to Yellow
  • 🟡 Yellow → cars slow down; transitions to Red
  • 🔴 Red → cars stop; transitions to Green

The signal object is the same. Its behavior changes with state. Without the State pattern, you'd track state in a string and branch everywhere on it.


Class Diagram#


Violation Code — The Problem#

java
class TrafficSignal {
    private String state = "GREEN";

    public void change() {
        if (state.equals("GREEN")) {
            System.out.println("Green: Go!");
            state = "YELLOW";
        } else if (state.equals("YELLOW")) {
            System.out.println("Yellow: Slow down!");
            state = "RED";
        } else if (state.equals("RED")) {
            System.out.println("Red: Stop!");
            state = "GREEN";
        }
        // Adding BLINKING state means editing this method ❌
    }
}

Issues:

  1. Tightly coupled transitions — all state logic is crammed into one class
  2. Hard to extend — adding a BLINKING state means modifying existing code
  3. Violates OCP — the class must be changed to add new states
  4. Difficult to test — can't test individual states in isolation
  5. Hard to reuse — state behavior can't be reused elsewhere

Enhanced Code — State Pattern#

java
// State interface
public interface TrafficLightState {
    void change(TrafficLight context);
}

// Concrete states
public class GreenState implements TrafficLightState {
    @Override
    public void change(TrafficLight context) {
        System.out.println("Green: Go! → switching to Yellow");
        context.setState(new YellowState());
    }
}

public class YellowState implements TrafficLightState {
    @Override
    public void change(TrafficLight context) {
        System.out.println("Yellow: Slow down! → switching to Red");
        context.setState(new RedState());
    }
}

public class RedState implements TrafficLightState {
    @Override
    public void change(TrafficLight context) {
        System.out.println("Red: Stop! → switching to Green");
        context.setState(new GreenState());
    }
}

// Context — delegates behavior to current state
public class TrafficLight {
    private TrafficLightState currentState;

    public TrafficLight() {
        this.currentState = new GreenState(); // initial state
    }

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

    public void change() {
        currentState.change(this); // delegate to current state
    }
}

// Client
public class Main {
    public static void main(String[] args) {
        TrafficLight light = new TrafficLight();
        light.change(); // Green → Yellow
        light.change(); // Yellow → Red
        light.change(); // Red → Green
    }
}

Adding a BlinkingState → one new class, zero existing classes touched.


Common LLD Problems Using State Pattern#

1. Vending Machine#

  • States: IdleState, HasMoneyState, DispensingState, OutOfStockState
  • Context: Machine accepts coins, dispenses items, or rejects input based on its current state.

2. Media Player#

  • States: PlayingState, PausedState, StoppedState
  • Context: The same button (play/pause) behaves differently depending on whether media is playing or paused.

3. ATM Machine#

  • States: NoCardState, CardInsertedState, PinEnteredState, TransactionState
  • Context: The ATM's available operations depend entirely on where the user is in the flow.

4. Order Processing System#

  • States: OrderPlaced, OrderConfirmed, OrderShipped, OrderDelivered, OrderCancelled
  • Context: Each state enables different transitions and actions (confirm, ship, cancel).

5. Online Document Editor#

  • States: EditMode, ReadOnlyMode, CommentMode
  • Context: UI and editing capabilities change based on current mode.

6. Game Character#

  • States: IdleState, RunningState, JumpingState, AttackingState
  • Context: A character responds to the same input differently depending on its current state.

7. TCP Connection#

  • States: Closed, Listen, Established, FinWait, TimeWait
  • Context: Network behavior is defined entirely by the protocol state.

8. Door Lock System#

  • States: LockedState, UnlockedState, AlarmState
  • Context: Wrong pin in LockedState triggers AlarmState; correct pin in LockedState transitions to UnlockedState.

State vs Strategy#

Both delegate behavior to objects, but the intent differs:

StateStrategy
Who changes?Object transitions itselfClient swaps algorithms
State awarenessStates can know about each otherStrategies are independent
PurposeModel state-dependent behaviorSwap interchangeable algorithms

ReferencesLinks
Article ReferenceRefactoring Guru — State