Design Patterns State

The State pattern lets an object change its behavior when its internal condition changes. From outside, it appears to switch classes at runtime. Long chains of if-else checks disappear, and each condition earns its own small class.

The Real-World Analogy

A traffic light acts differently in each color. Red stops vehicles, green releases them, and yellow warns drivers. The light itself stays one device, and its current color decides what happens next.

Problem Without State

class Order {
    status = "NEW"

    ship() {
        if status == "NEW"        → error: pay first
        else if status == "PAID"  → send package, status = "SHIPPED"
        else if status == "SHIPPED" → error: already shipped
        else if status == "CANCELLED" → error: order closed
    }
    cancel() { ...another long chain... }
    pay()    { ...another long chain... }
}

Every new status forces edits to every method. The class grows tangled and fragile.

Solution With State

┌──────────────┐  delegates   ┌──────────────────┐
│ Order        │ ───────────▶ │ «interface»      │
│ - state      │              │ OrderState       │
│ + ship()     │              │ + pay()          │
│ + cancel()   │              │ + ship()         │
└──────────────┘              │ + cancel()       │
                              └────────▲─────────┘
                    ┌──────────────────┼───────────────────┐
              ┌─────┴─────┐     ┌──────┴─────┐      ┌──────┴──────┐
              │ NewState  │     │ PaidState  │      │ShippedState │
              └───────────┘     └────────────┘      └─────────────┘

The Order forwards each request to its current state object. Each state class holds the rules for one condition only.

State Transition Diagram

            pay()                  ship()
  ┌─────┐ ─────────▶ ┌──────┐ ─────────▶ ┌─────────┐
  │ NEW │            │ PAID │            │ SHIPPED │
  └──┬──┘            └──┬───┘            └─────────┘
     │ cancel()         │ cancel()
     ▼                  ▼
  ┌───────────────────────────┐
  │        CANCELLED          │
  └───────────────────────────┘

Arrows show the legal moves. Any move missing from the diagram becomes an error message inside the matching state class.

Code Example

interface OrderState {
    void pay(Order order);
    void ship(Order order);
}

class NewState implements OrderState {
    public void pay(Order order)  { order.setState(new PaidState()); print("Payment received"); }
    public void ship(Order order) { print("Pay before shipping"); }
}

class PaidState implements OrderState {
    public void pay(Order order)  { print("Already paid"); }
    public void ship(Order order) { order.setState(new ShippedState()); print("Package sent"); }
}

class ShippedState implements OrderState {
    public void pay(Order order)  { print("Already paid"); }
    public void ship(Order order) { print("Already shipped"); }
}

class Order {
    private OrderState state = new NewState();
    void setState(OrderState s) { state = s; }
    void pay()  { state.pay(this); }
    void ship() { state.ship(this); }
}

Order o = new Order();
o.ship();   // Pay before shipping
o.pay();    // Payment received
o.ship();   // Package sent

Who Controls Transitions

State classes often decide the next state themselves, as the example shows. A central table inside the context class offers another option. Small machines work well with the first style, and larger machines gain clarity from the second.

Real Projects Using State

ScenarioStates
Media playerStopped, playing, paused
Document workflowDraft, review, published
Vending machineIdle, has money, dispensing
Network connectionClosed, connecting, open

State Versus Strategy

Both patterns hold a swappable object. Strategy gets chosen by the client and stays put. State changes by itself as events happen, and states often know about each other.

Advantages and Disadvantages

AdvantagesDisadvantages
Removes giant condition chainsMore classes for small machines
New states join without edits to old onesTransition logic spreads across files
Each state stays easy to testOverkill for two or three states

Step-by-Step Walkthrough

Trace the three lines at the end of the code example. A new Order starts with a NewState object. The first ship() call reaches NewState.ship(), which prints a reminder to pay. A pay() call reaches NewState.pay(), which swaps the order's state to PaidState. Another ship() call lands in PaidState.ship(), which moves the order to ShippedState.

The Order class never checks a status flag. Each state object knows its own rules.

Designing a State Machine

  • List every state before writing code.
  • Draw the arrows for legal moves and label each with its trigger.
  • Mark final states that accept no further moves.
  • Decide what every state does when it receives an unexpected action.

Key Takeaways

  • State moves condition-specific behavior into separate classes.
  • The context object delegates every call to its current state.
  • A transition diagram clarifies the legal moves before coding starts.
  • Strategy stays fixed after selection, while State changes on its own.