State
Every method begins with if state == "no_coin": ... elif state == "has_coin": ... The same state checks are repeated in each method.
Make each state a small class with the methods for that state.
An object behaves differently in each of several states
The picture from life
A vending machine acts differently depending on whether a coin is inside. Press the button with no coin: "insert a coin". Press it with a coin: the snack drops. Same button, different behaviour, because the machine is in a different state.
The problem
What goes wrong when you do not use it.
Every method begins with if state == "no_coin": ... elif state == "has_coin": ... The same state checks are repeated in each method. Adding a "sold out" state means editing every method and risking a missed branch.
- An object behaves differently in each of several states
- Big if/switch blocks test the same state variable
- States have clear transitions (traffic light, order status)
The idea
How the pattern fixes it.
Make each state a small class with the methods for that state. The machine keeps a pointer to the current state object and forwards calls to it. A state changes the pointer to move to the next state.
- implements
- has a / holds
- calls / uses
Who is who
- Machine (context)
- Holds the current state and forwards calls.
- NoCoin, HasCoin
- One class per state with its own behaviour.
- Transitions
- A state sets machine.state to the next state.
Build it in steps
- 1List the states and the events.
- 2Write a class per state with a method per event.
- 3Let the context hold and forward to the current state.
- 4Make each method return or set the next state.
Watch it run
Step through the calls. Tap an arrow to jump to it.
The machine starts in NoCoin.
push()The code
Python and Java. Try writing the classes yourself from the diagram first.
class NoCoin:
def insert_coin(self, machine):
machine.state = HasCoin() # move to the next state
return "coin accepted"
def push(self, machine):
return "insert a coin first"
class HasCoin:
def insert_coin(self, machine):
return "coin already inserted"
def push(self, machine):
machine.state = NoCoin()
return "here is your snack"
class Machine:
def __init__(self):
self.state = NoCoin()
def insert_coin(self):
return self.state.insert_coin(self) # forward to the current state
def push(self):
return self.state.push(self)
def demo():
m = Machine()
return [m.push(), m.insert_coin(), m.insert_coin(), m.push(), m.push()]What you gain
- Each state's rules live in one class
- New states do not touch the old ones
What it costs
- More classes for a machine with few states
- Transitions are spread across state classes
You have already met it in
- Order lifecycle (placed, paid, shipped)
- TCP connection states
- Media players: playing, paused, stopped
Often mixed up with
Make it stick
Model a traffic light with Red, Green and Yellow states, each deciding its next state and how long it lasts.