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.
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#
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:
- Tightly coupled transitions — all state logic is crammed into one class
- Hard to extend — adding a BLINKING state means modifying existing code
- Violates OCP — the class must be changed to add new states
- Difficult to test — can't test individual states in isolation
- Hard to reuse — state behavior can't be reused elsewhere
Enhanced Code — State Pattern#
// 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:
| State | Strategy | |
|---|---|---|
| Who changes? | Object transitions itself | Client swaps algorithms |
| State awareness | States can know about each other | Strategies are independent |
| Purpose | Model state-dependent behavior | Swap interchangeable algorithms |
| References | Links |
|---|---|
| Article Reference | Refactoring Guru — State |