Deep Dive
low level designdesign patternsoop

State Pattern: Make Illegal Transitions Impossible

An order that can be paid, shipped, delivered, or cancelled grows a switch(status) block in every method — until one of them forgets an arm and a shipped order gets refunded. The State pattern moves both behavior and the legal next-steps into the state itself.

·16 min read
Medium

Every commerce backend has an Order class, and every Order class has a String status. It starts as three values and one method. Then the lifecycle grows: CREATED, PAID, SHIPPED, DELIVERED, CANCELLED. And every method — pay(), ship(), cancel(), refund(), deliver() — grows its own switch(status) to decide what’s allowed from here.

The bug is never in the switch you’re looking at. It’s in the one you forgot. Someone adds a RETURNED status for a new returns flow, updates refund() and deliver(), and misses the arm in cancel(). Now a returned order can be cancelled, which double-refunds the customer. The rule “a shipped order cannot be cancelled” was never written down in one place — it was scattered across five methods, so no single edit could keep it true.

The problem isn’t the number of states. It’s that the behavior for a mode and the legal transitions out of that mode are smeared across every method instead of living together. The Gang of Four named the fix.

The intuition: a traffic light knows what comes next

A traffic light isn’t a controller holding a color variable and a big switch(color). Think of it as three objects. Green knows one thing: after its interval, hand control to Yellow. Yellow knows: next is Red. Red knows: next is Green. No single “light manager” enumerates the whole cycle — each color owns its own behavior and the single arrow out of it.

That’s the whole pattern. The light is the context — it just holds “the current color” and delegates. Each color is a state — and crucially, a state can promote the context to the next state. Green doesn’t ask permission to become Yellow; it makes it happen.

The members-only continuation takes it from intuition to running code: the switch(status) smell shown in full, the context/contract/self-promoting-state implementation, a class-diagram walkthrough of the finite-state machine, an order traced through its whole lifecycle, the State-vs-Strategy tell, the tradeoffs, a vending-machine refactor challenge, and the interview corner.

Members only

Keep reading with Premium

You've reached the members-only part of this deep-dive — the full implementation, the interactive ring simulator, and the step-by-step walkthrough. Unlock it with a membership.

Related Articles