Skip to content
LLD Learn/Behavioral Patterns
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

State

7 min read

You'll learn to

  • -Model an object whose behavior changes with its internal state (vending machine, traffic light, order lifecycle) without a giant switch statement
  • -Implement state transitions as a set of classes, each owning only the behavior valid in that state

State lets an object alter its behavior when its internal state changes, appearing to change its class. In practice, it replaces a giant switch statement on a status field with a set of classes, one per state, each of which owns exactly the behavior that is valid in that state.

The Problem: State-Dependent Behavior as Conditionals

The pattern this replaces - branching on state everywhere
class VendingMachine:
    def __init__(self):
        self.state = "idle"

    def insert_coin(self):
        if self.state == "idle":
            self.state = "has_coin"
        elif self.state == "has_coin":
            print("Coin already inserted")
        elif self.state == "dispensing":
            print("Please wait, dispensing in progress")
        # every new state means editing every method that branches on state

The State Pattern Version

Each state owns its own transitions - no central switch statement
class VendingMachineState(ABC):
    @abstractmethod
    def insert_coin(self, machine: "VendingMachine") -> None: ...
    @abstractmethod
    def select_item(self, machine: "VendingMachine") -> None: ...

class IdleState(VendingMachineState):
    def insert_coin(self, machine):
        machine.state = HasCoinState()
    def select_item(self, machine):
        print("Insert a coin first")

class HasCoinState(VendingMachineState):
    def insert_coin(self, machine):
        print("Coin already inserted")
    def select_item(self, machine):
        machine.state = DispensingState()

class DispensingState(VendingMachineState):
    def insert_coin(self, machine):
        print("Please wait, dispensing in progress")
    def select_item(self, machine):
        print("Already dispensing")

class VendingMachine:
    def __init__(self):
        self.state: VendingMachineState = IdleState()   # delegates every call to current state

    def insert_coin(self) -> None:
        self.state.insert_coin(self)

    def select_item(self) -> None:
        self.state.select_item(self)

Why This Scales Better Than the Conditional Version

Adding a new state (OutOfStockState) in the conditional version means finding and editing every method that branches on state.state string values - easy to miss a spot. In the State pattern version, adding OutOfStockState means writing one new class that implements the shared interface; VendingMachine itself never changes, and no existing state class needs to be touched. This is the exact same Open/Closed win Strategy provides, applied specifically to state-dependent behavior.

Revisit the Strategy chapter's distinction: the structure here looks nearly identical to Strategy (an interface, a holder, delegation), but the states themselves drive transitions (IdleState.insert_coin() sets machine.state = HasCoinState()) rather than a caller choosing the "algorithm" from outside.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

ScaleDojo Logo
Initializing ScaleDojo