State Pattern

Give each mode of an object its own class, so the object changes behavior by swapping the state object instead of branching on a flag.

Overview

The problem: behavior that depends on a mode

Some objects behave differently depending on what mode they are in. A vending machine ignores a button press until money is inserted. A document cannot be published while it is still a draft. A media player treats the play button as pause while it is playing.

The first version of such code keeps a mode field and checks it inside every method. Each new event adds a branch to every method, and each new mode adds a branch to every event. The table below is the whole behavior of a small vending machine. Every cell is one branch somewhere in the code.

  • Idle — go to Has money — nothing happens — nothing happens
  • Has money — stay, add to credit — dispense, go to Dispensing — nothing happens
  • Dispensing — refused — refused — go to Idle

Three modes and three events give nine cells. Six modes and six events give thirty-six. Spread across methods as if and elif, that many branches is where bugs hide.

The pattern

The State pattern is one of the 23 patterns in the 1994 book Design Patterns by Gamma, Helm, Johnson and Vlissides (the Gang of Four). The idea is to turn each row of the table into a class. The object that has modes is the context. It holds one state object and forwards every mode-dependent call to it. Each concrete state class implements the events for one mode, and says which state comes next.

The context's public methods become one line each: call the current state, and store what it returns. The branches disappear, because the language's method dispatch picks the right behavior for the right mode.

The state machine behind it

A State-pattern class is the code form of a state machine diagram. Drawing the diagram first is the fastest way to get the classes right: each box is a class, and each arrow is a method that returns a different state.

A runnable example

In this version each state method returns the next state. The context never decides transitions itself. Unhandled events return the same state, so the machine ignores them instead of crashing.

Note how the test reads like the diagram. Each assert checks one arrow, or checks that a missing arrow really does nothing.

State compared with Strategy

The two patterns have the same shape: a context that holds one object behind an interface and delegates to it. The difference is who changes the object, and how often.

  • Who picks the object — The states themselves, as events arrive — The client, when it configures the context
  • How often it changes — Constantly, during the object's life — Rarely; often set once
  • Do the objects know each other — Yes, a state names its successors — No, strategies are independent
  • The question it answers — Where is this object in its lifecycle — Which algorithm should be used

If a behavior switch is triggered from outside, it is Strategy. If the object's own actions trigger it, it is State.

When to use it, and when a simpler tool wins

  • Two or three modes, a handful of events — An enum field and a match or switch in each method
  • Many modes, or modes that keep being added — State pattern, one class per mode
  • Transitions are the hard part, and states have little behavior — A transition table: a dict from (state, event) to the next state
  • Entry and exit actions, timers, nested states — A state machine library, or State with enter and exit methods

The pattern costs one class per mode. When the table of modes and events fits on a napkin, a plain conditional is honest and clear. Reach for State when the conditionals start to spread across many methods.

What you will be able to do

  • Recognize mode-dependent conditionals and know when they justify the pattern.
  • Draw the state diagram first, then map each box to a class and each arrow to a method.
  • Decide whether states or the context own the transitions, and defend the choice.
  • Tell State from Strategy in one sentence.
  • Write tests that check every transition and every ignored event.

Interview questions

  • Show the smell the State pattern fixes, and the fix.
  • Who should own the transitions, the states or the context?
  • Can state objects be shared between contexts?
  • How do you test a class built with the State pattern?
  • Where do entry and exit actions go?

Sign up free to read the full topic

More Low-Level Design topics

Start free on Cariara · Coding problems · System design studio