State Pattern — Avoiding Too Many Conditionals
Model behaviour as explicit states when an object responds differently as its internal state changes.
We often build a class whose behaviour changes according to a combination of internal values. The first implementation usually relies on a growing collection of if statements or a large switch. As more modes and actions are added, the logic becomes difficult to understand and maintain.
The Gang of Four State pattern addresses this problem by allowing an object to change its behaviour when its internal state changes.
A clock with two buttons
Imagine a digital clock with two actions: MODE and CHANGE. Pressing MODE moves the clock through three states:
- normal display;
- updating the hour;
- updating the minutes.
The meaning of CHANGE depends on the current state:
- in normal display, it turns on the light;
- while updating the hour, it increments the hour;
- while updating the minutes, it increments the minutes.
A conditional implementation might begin like this:
void pressChange() {
if (state == NORMAL_DISPLAY) {
activateLight();
} else if (state == UPDATING_HOURS) {
hours++;
} else if (state == UPDATING_MINUTES) {
minutes++;
}
}
The code works, but every new state forces us to revisit several operations. The rules that belong to one state are scattered across methods, making it difficult to see the complete behaviour of the clock.
Structure of the pattern

The pattern separates three roles:
- Context is the object used by the client. It stores a reference to the current state and delegates state-dependent actions to it.
- State is an interface or abstract class defining the actions shared by every possible state.
- Concrete State represents one mode of the context. It implements those actions and may move the context to another state.
For the clock, the context delegates both buttons:
public class Clock {
private ClockState state;
int hour;
int minute;
public Clock() {
state = new NormalDisplayState(this);
}
void setState(ClockState state) {
this.state = state;
}
public void pressMode() {
state.pressMode();
}
public void pressChange() {
state.pressChange();
}
}
The common contract is small:
public abstract class ClockState {
protected final Clock clock;
protected ClockState(Clock clock) {
this.clock = clock;
}
public abstract void pressMode();
public abstract void pressChange();
}
Each concrete state keeps its own rules together:
public class NormalDisplayState extends ClockState {
public NormalDisplayState(Clock clock) {
super(clock);
}
public void pressMode() {
clock.setState(new UpdatingHourState(clock));
}
public void pressChange() {
System.out.println("Light on");
}
}
public class UpdatingHourState extends ClockState {
public UpdatingHourState(Clock clock) {
super(clock);
}
public void pressMode() {
clock.setState(new UpdatingMinuteState(clock));
}
public void pressChange() {
clock.hour = (clock.hour + 1) % 24;
}
}
public class UpdatingMinuteState extends ClockState {
public UpdatingMinuteState(Clock clock) {
super(clock);
}
public void pressMode() {
clock.setState(new NormalDisplayState(clock));
}
public void pressChange() {
clock.minute = (clock.minute + 1) % 60;
}
}
What this buys us
Each state now describes a coherent set of behaviours and transitions. Adding a state still requires deliberate design, but it no longer requires adding another branch to every state-dependent method in the context.
The pattern is most useful when states are meaningful concepts with substantial, changing behaviour. For a small fixed condition, a simple if or switch may remain clearer. The goal is not to remove conditionals at any cost; it is to make the state machine visible when the state machine is already part of the problem.
References
- Game Programming Patterns: State
- Design Patterns: Elements of Reusable Object-Oriented Software, Gamma et al.