State Pattern
Scattered conditionals checking a status field
A turnstile has two states: locked and unlocked. It reacts to two events: a coin is inserted, or a person pushes through. In the naive implementation, both event handlers check the current state and branch on it:
1# Bad: every method must check self.state and branch.
2# Adding a third state (broken, maintenance) multiplies every method.
3class Turnstile:
4 def __init__(self) -> None:
5 self.state = "LOCKED"
6
7 def insert_coin(self) -> None:
8 if self.state == "LOCKED":
9 self.state = "UNLOCKED"
10 print("Coin accepted — turnstile unlocked.")
11 elif self.state == "UNLOCKED":
12 print("Already unlocked — coin returned.")
13
14 def push(self) -> None:
15 if self.state == "LOCKED":
16 print("Locked — please insert a coin.")
17 elif self.state == "UNLOCKED":
18 self.state = "LOCKED"
19 print("Passage granted — turnstile locked.")
The problem is structural. The state lives as a plain string, and every method has to enumerate
every state it might be in — so the behavior of a single state, like what LOCKED does on each
event, is smeared across multiple methods instead of gathered in one place. And the conditionals
multiply as the machine grows: add a third state — say, a BROKEN state that ignores both events
and displays an error — and every method grows another branch; a fourth multiplies the branching
again. The total complexity grows as states × events.
How it works
The State pattern moves each state's behavior into its own class. The context (Turnstile) holds
a reference to the current state object and delegates both events to it. Transitions are
expressed by swapping the state object, not by mutating a string field.
The class structure mirrors the diagram: one class per state, each implementing the same two-method interface.
Adding a BrokenState means adding one class, not editing every existing method. The complexity
of the code grows as states + events, not as states × events.
Tracing a coin-insert and then a push shows how the context delegates each event to the current state object, which handles it and swaps the context's state when a transition occurs.