design·lab

Behavioral pattern

State

An object's behavior changes with its internal state — model each state as its own object instead of branching on a status flag everywhere.

✗ The problem

Behavior branches on a status string

The same if / else if chain on status gets repeated in every method — pay(), ship(), cancel()

class Order {
  pay() {
    if (this.status === 'pending') this.status = 'paid';
    else if (this.status === 'paid') { /* no-op */ }
    else throw new Error('bad state');
  }
  ship() {
    if (this.status === 'paid') this.status = 'shipped';
    else if (this.status === 'pending') throw new Error('pay first');
    // cancel() repeats the same chain again…
  }
}
Adding one state means editing every method. Illegal transitions like ship() before pay() are easy to allow by mistake.
✓ The pattern

Each state is its own object

A state object knows only its own legal moves. The context (Order) just delegates to whichever state object is current.

class PendingState {
  pay(order)  { order.setState(new PaidState()); }
  ship(order) { throw new Error('pay first'); }
}
// Order.pay() just calls: this.state.pay(this)
Pending
Paid
Shipped
Delivered
✓ See it live

Fire an event — only legal moves happen

The order starts Pending. Click an event; the current state object decides what's next — illegal events are rejected.

Pending
Paid
Shipped
Delivered
Cancelled
from Pending / Paid
Current state: Pending
✓ Takeaway

Explicit states, impossible transitions

  • Encapsulate state-specific behavior inside each state object, not the context.
  • Transitions become explicit — a state that doesn't implement an event simply can't do it.
  • Kills the giant if / else if chain repeated across every method.
  • Looks like Strategy structurally, but intent differs: states swap themselves as the object's condition changes; a Strategy is picked by the client.
  • Careful: many states means many small classes — worth it once branching gets messy.
🎯 Principle applied: State applies Open/Closed (a new state is a new class; existing states untouched) and SRP (each state owns its behavior), replacing sprawling status conditionals.