← Back to Design Patterns
🚦
Behavioral

State

Allows an object to change its behavior when its internal state changes — appears to change class.

The Problem

An object's behavior depends on its current state, leading to large if/else or switch statements scattered throughout methods that all check the current state.

How It Works

  • ›Each state is a separate class that encapsulates behavior for that state
  • ›State transitions happen by replacing the current state object
  • ›Eliminates complex conditional logic scattered across methods
  • ›New states can be added without modifying existing state classes (Open/Closed)
  • ›Context delegates all state-dependent behavior to the current state object

When to Use

✓Object's behavior must change based on its state at runtime
✓Finite state machines: order lifecycle, connection states, player states
✓When state-specific code is duplicated across many conditional branches
✓When transitions between states need to be explicit and well-defined

Real-World Examples

🍎
iOS

UIViewController lifecycle (viewDidLoad → viewWillAppear → viewDidAppear), AVPlayer state

🤖
Android

Fragment backstack, MediaPlayer states (Idle→Initialized→Prepared→Started), ViewModel state flows

🌐
Web

Promise states (pending/fulfilled/rejected), XState FSM, React component lifecycle

Code Examples

Swift
protocol OrderState {
    var label: String { get }
    func next(order: Order)
}

class Order {
    var state: OrderState = PendingState()
    func advance() { state.next(order: self) }
    func status()  { print("Order: \(state.label)") }
}

class PendingState: OrderState {
    var label = "Pending"
    func next(order: Order) { order.state = ProcessingState() }
}
class ProcessingState: OrderState {
    var label = "Processing"
    func next(order: Order) { order.state = ShippedState() }
}
class ShippedState: OrderState {
    var label = "Shipped"
    func next(order: Order) { order.state = DeliveredState() }
}
class DeliveredState: OrderState {
    var label = "Delivered"
    func next(order: Order) { print("Already delivered") }
}

let order = Order()
order.status()   // "Order: Pending"
order.advance(); order.status()   // "Order: Processing"
order.advance(); order.status()   // "Order: Shipped"
order.advance(); order.status()   // "Order: Delivered"