The State Pattern - Objects That Change Their Behaviour
The State Pattern
The State pattern allows an object to alter its behaviour when its internal state changes. The object appears to change its class at runtime. Instead of writing long chains of conditionals to determine what to do based on the current state, you encapsulate each state in its own class and delegate behaviour to that state object.
This eliminates complex conditional logic, makes state transitions explicit, and lets you add new states without modifying existing code.
How It Works
The pattern defines a State interface and a Context that holds a reference to the current state. The Context delegates every request to the current state object. Each concrete state implements behaviour for its state and can trigger transitions to the next state by calling a setter on the context.
+------------------+
| Context |
| - currentState |
| + request() |
+------------------+n| |
| delegates to |
+--------+---------+
|n+-------------------+ +-------------------+
| StateA |---->| StateB |
| handle(context) | | handle(context) |
| transitions to B | | transitions to A |
+-------------------+ +-------------------+
When the context receives a request, it calls currentState.handle(this). The state object processes the request and may call context.setState(new StateB()) to change the context's behaviour for the next request.
Java Implementation
Start with the State interface and the Context class.
public interface State {
void handle(VendingMachine context);
}
public class VendingMachine {
private State currentState;
public VendingMachine() {
this.currentState = new IdleState();
}
public void setState(State state) {
this.currentState = state;
}
public void insertCoin() {
currentState.handle(this);
}
}
The VendingMachine holds a currentState field and delegates insertCoin() to whatever state object it currently holds. The initial state is IdleState.
Concrete States
Each concrete state implements the State interface and defines its own behaviour plus the logic to transition to the next state.
public class IdleState implements State {
public void handle(VendingMachine context) {
System.out.println("Coin inserted. Ready to select.");
context.setState(new HasCoinState());
}
}
public class HasCoinState implements State {
public void handle(VendingMachine context) {
System.out.println("Dispensing item. Returning to idle.");
context.setState(new IdleState());
}
}
IdleState prints a message and transitions to HasCoinState. HasCoinState dispenses the item and transitions back to IdleState. Notice there are no if-else chains - each class owns exactly one state's logic.
Real-World Scenario
Think of a TCP connection. A connection can be in states like LISTEN, SYN_SENT, ESTABLISHED, or FIN_WAIT. Each state determines which operations are valid. Trying to send data while in LISTEN state is an error. The State pattern models this cleanly: each state class enforces its own rules and transitions to the next state when events occur, rather than scattering switch statements throughout a monolithic connection class.
Key Points
- The State pattern encapsulates each state in a separate class, replacing complex conditional logic with polymorphism.
- The Context delegates behaviour to its current state object and can switch states at runtime.
- Each concrete state decides what to do and which state to transition to next.
- Adding a new state means creating a new class - no existing state classes need to change.
- The pattern is ideal for objects with well-defined state machines such as connections, UI flows, and order processing.