Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

State Pattern

Let objects change behavior when their state changes - no more giant if/else state machines!

State Pattern: Behavior That Changes with State

Section titled “State Pattern: Behavior That Changes with State”

Now let’s dive into the State Pattern - one of the most elegant behavioral design patterns that allows an object to alter its behavior when its internal state changes. The object will appear to change its class!

Imagine a vending machine. When it has no money inserted, pressing the dispense button does nothing. When money is inserted, pressing dispense gives you a product. When it’s out of stock, it refunds your money. The machine behaves differently based on its state. The State Pattern works the same way!

The State Pattern allows an object to change its behavior when its internal state changes. Instead of having giant if/else or switch statements checking the state, each state becomes its own class that knows how to handle requests for that state.

The State Pattern is useful when:

  1. Object behavior depends on state - Different actions based on internal state
  2. You have complex state-dependent logic - Many if/else checking state
  3. State transitions are well-defined - Clear rules for state changes
  4. You want to add new states easily - Without modifying existing code
  5. States should be explicit - Each state is a first-class object

What Happens If We Don’t Use State Pattern?

Section titled “What Happens If We Don’t Use State Pattern?”

Without the State Pattern, you might:

  • Giant switch/if-else statements - Checking state everywhere
  • Scattered state logic - Same checks in multiple methods
  • Hard to add states - Modify many places for new state
  • Violation of OCP - Need to modify existing code
  • Difficult to understand - State behavior spread across methods

Let’s start with a super simple example that anyone can understand!

Diagram

Here’s how the document transitions between states:

stateDiagram-v2
    [*] --> Draft: Create Document
    Draft --> Draft: edit()
    Draft --> Moderation: publish()
    Moderation --> Published: approve()
    Moderation --> Draft: reject()
    Published --> Draft: edit()
    Published --> [*]: archive()
    
    note right of Draft: Can edit freely
    note right of Moderation: Under review
    note right of Published: Live content
sequenceDiagram
    participant Client
    participant Document
    participant DraftState
    participant ModerationState
    participant PublishedState
    
    Client->>Document: new Document()
    Document->>DraftState: set initial state
    
    Client->>Document: edit("New content")
    activate Document
    Document->>DraftState: edit(document)
    DraftState->>DraftState: Update content
    DraftState-->>Document: Content updated
    deactivate Document
    
    Client->>Document: publish()
    activate Document
    Document->>DraftState: publish(document)
    DraftState->>Document: set_state(ModerationState)
    DraftState-->>Document: Sent to moderation
    deactivate Document
    
    Note over Client,PublishedState: State changed! Same method, different behavior
    
    Client->>Document: publish()
    activate Document
    Document->>ModerationState: publish(document)
    ModerationState->>Document: set_state(PublishedState)
    ModerationState-->>Document: Published!
    deactivate Document

You’re building a document management system where documents go through different states (Draft, Moderation, Published). Without State Pattern:

Problems:

  • State checks scattered across every method
  • Adding a new state requires modifying ALL methods
  • Hard to understand what each state does
  • Violates Open/Closed Principle
classDiagram
    class DocumentState {
        <<interface>>
        +edit(doc, content) void
        +publish(doc) void
        +reject(doc) void
    }
    class DraftState {
        +edit(doc, content) void
        +publish(doc) void
        +reject(doc) void
    }
    class ModerationState {
        +edit(doc, content) void
        +publish(doc) void
        +reject(doc) void
    }
    class PublishedState {
        +edit(doc, content) void
        +publish(doc) void
        +reject(doc) void
    }
    class Document {
        -state: DocumentState
        -content: str
        +set_state(state) void
        +edit(content) void
        +publish() void
        +reject() void
    }
    
    DocumentState <|.. DraftState : implements
    DocumentState <|.. ModerationState : implements
    DocumentState <|.. PublishedState : implements
    Document --> DocumentState : delegates to
    
    note for DocumentState "Each state knows how\nto handle requests"
    note for Document "Context delegates\nto current state"

Real-World Software Example: Order Processing System

Section titled “Real-World Software Example: Order Processing System”

Now let’s see a realistic software example - an e-commerce order processing system with multiple states.

You’re building an order system where orders go through states: Pending → Paid → Shipped → Delivered (or Cancelled at various points). Without State Pattern:

Problems:

  • 5 states × 4 methods = 20 if-else branches!
  • Adding a new state requires modifying ALL methods
  • State behavior scattered across methods
  • Very error-prone and hard to maintain
stateDiagram-v2
    [*] --> Pending: Create Order
    Pending --> Paid: pay()
    Pending --> Cancelled: cancel()
    Paid --> Shipped: ship()
    Paid --> Cancelled: cancel() + refund
    Shipped --> Delivered: deliver()
    Delivered --> [*]: Complete
    Cancelled --> [*]: Complete
    
    note right of Pending: Awaiting payment
    note right of Paid: Ready to ship
    note right of Shipped: In transit
    note right of Delivered: Complete!
    note right of Cancelled: Order cancelled

There are different ways to implement the State Pattern:

When states are stateless, they can be singletons:

Pros: Memory efficient, no unnecessary object creation
Cons: States can’t hold instance-specific data

Track state history for auditing:

States can have sub-states:


Use State Pattern when:

Object behavior depends on state - Different actions for different states
You have many state-checking conditionals - if/else chains everywhere
State transitions are well-defined - Clear rules for state changes
You want to add states easily - Without modifying existing code
States should be explicit - First-class state objects

Don’t use State Pattern when:

Few states with simple transitions - Direct if/else might be clearer
State doesn’t affect behavior - Just data, not behavior
States are not well-defined - Unclear transitions
Over-engineering - Don’t add complexity for 2-3 simple states


Mistake 2: Context Knowing About All Concrete States

Section titled “Mistake 2: Context Knowing About All Concrete States”

Mistake 3: Missing Default Behavior in States

Section titled “Mistake 3: Missing Default Behavior in States”

  1. No if/else chains - Each state handles its own behavior
  2. Easy to add states - Just create a new state class
  3. Single Responsibility - Each state has one job
  4. Open/Closed Principle - Add states without modifying existing code
  5. Explicit state machine - State diagram maps to code
  6. Testable - Each state can be tested independently

State Pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. The object appears to change its class.

  • Eliminate if/else chains - State checks spread everywhere
  • Easy to add states - New state = new class
  • Clear state behavior - All behavior in one place
  • Explicit transitions - States control transitions
  • Follow Open/Closed Principle
  1. Define State interface - Methods for all state behaviors
  2. Create Concrete States - Each state implements interface
  3. Create Context - Holds current state, delegates to it
  4. States change Context state - Transitions happen in states
  5. Client uses Context - Doesn’t know about states
Context → State Interface → Concrete States
  • State - Interface for state behavior
  • Concrete State - Specific state implementation
  • Context - Object whose behavior changes
  • Client - Uses context, unaware of states
class State(ABC):
@abstractmethod
def handle(self, context): pass
class ConcreteStateA(State):
def handle(self, context):
print("State A behavior")
context.set_state(ConcreteStateB())
class Context:
def __init__(self):
self._state = ConcreteStateA()
def set_state(self, state):
self._state = state
def request(self):
self._state.handle(self)

✅ Object behavior depends on state
✅ Many if/else checking state
✅ Well-defined state transitions
✅ Need to add states easily
✅ States should be explicit

❌ Few simple states
❌ State doesn’t affect behavior
❌ Unclear transitions
❌ Over-engineering

  • State Pattern = Behavior changes with state
  • State = Encapsulates state-specific behavior
  • Context = Delegates to current state
  • Benefit = No if/else, easy to extend
  • Principle = Open for extension, closed for modification

What to say:

“State Pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. Instead of having if/else chains checking state, each state becomes its own class that handles behavior for that state. The object appears to change its class.”

Why it matters:

  • Shows you understand the fundamental purpose
  • Demonstrates knowledge of eliminating conditionals
  • Indicates you can explain concepts clearly

Must discuss:

  • State - Object changes behavior based on INTERNAL state, automatic
  • Strategy - Client chooses behavior EXTERNALLY, explicit
  • Key difference - Who controls the change and why

Example to give:

“State Pattern is like a traffic light that automatically changes behavior based on its internal state - red means stop, green means go. Strategy Pattern is like choosing a route on GPS - YOU explicitly choose the algorithm. With State, transitions happen automatically based on internal logic. With Strategy, you explicitly set which algorithm to use.”

Benefits to mention:

  • No if/else chains - Clean, maintainable code
  • Easy to add states - Just create new class
  • Single Responsibility - Each state handles its behavior
  • Open/Closed Principle - Add without modifying
  • Explicit state machine - Code mirrors state diagram

Trade-offs to acknowledge:

  • More classes - Each state is a class
  • Overkill for simple states - 2-3 states might not need it
  • Can be complex - Many states with many methods

Q: “How does State Pattern differ from Strategy Pattern?”

A:

“Both encapsulate behavior in separate classes, but State changes behavior based on internal state automatically, while Strategy lets the client explicitly choose the algorithm. State objects typically know about other states and handle transitions. Strategy objects are usually stateless and interchangeable. State is about ‘what state am I in?’, Strategy is about ‘which algorithm should I use?’”

Q: “Where would you use State Pattern in a real system?”

A:

“I’d use State Pattern for order processing systems (pending, paid, shipped, delivered), document workflows (draft, review, published), connection handling (connecting, connected, disconnected), or UI components (enabled, disabled, loading). Any system where objects go through well-defined states with different behavior.”

Q: “How do you handle state transitions?”

A:

“State transitions can be handled in two ways: 1) States handle their own transitions - each state knows what the next state should be and calls context.setState(). 2) Context handles transitions - context decides next state based on rules. The first approach is cleaner because each state encapsulates its transition logic.”

Before your interview, make sure you can:

  • Define State Pattern clearly in one sentence
  • Explain when to use it (with examples)
  • Compare State vs Strategy Pattern
  • Implement State Pattern from scratch
  • List benefits and trade-offs
  • Connect to SOLID principles
  • Identify when NOT to use it
  • Give 2-3 real-world examples
  • Discuss state transition approaches
  • Draw a state diagram for an example

Remember: State Pattern is about letting objects change behavior when their state changes - no more giant if/else state machines! 🔄