Design Pattern 24: State Pattern - Complete Guide with Real-World Examples

πŸ“ Download the complete Design Pattern series code from our design_pattern repository.


🎯 What is the State Pattern?

The State Pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. The object will appear to change its class, making it perfect for implementing state machines and managing complex state-dependent behavior.

Key Use Cases:

  • βœ… State machines and workflow engines
  • βœ… Game development (character states, AI behavior)
  • βœ… UI components (button states, form validation)
  • βœ… Network protocols (connection states)
  • βœ… Business logic (order processing, workflow management)

πŸš€ Real-World Problem: Water Dispenser State Management

Let’s design a water dispenser system with the following requirements:

System Requirements:

  • Three operational states:
    • Heating: Raises water temperature to hot
    • Cooling: Lowers water temperature to cold
    • Standby: Maintains current water temperature
  • User interaction: Button presses to switch states
  • State-specific behavior: Each state performs appropriate actions

Business Rules:

  • Heating state cannot cool water simultaneously
  • Cooling state cannot heat water simultaneously
  • Standby state maintains current temperature
  • State transitions should be smooth and predictable

πŸ—οΈ Object-Oriented Analysis (OOA)

Let’s analyze the problem and identify the core components:

Identified Forces:

  1. High Coupling
    • State logic mixed with water dispenser core functionality
    • Difficult to maintain and modify state behavior
  2. Single Responsibility Principle Violation
    • Water dispenser class handles both state logic and core functionality
    • Class becomes overloaded with responsibilities
  3. Extension Challenges
    • Adding or modifying states requires changing core logic
    • Violates Open-Closed Principle (OCP)

πŸ’‘ State Pattern Solution

After analyzing the forces, we can apply the State Pattern to encapsulate state logic into separate classes:

State Pattern Components:

  1. State Interface
    • Defines common interface for all states
    • Ensures consistent behavior across states
  2. Concrete States
    • Each state implements the interface
    • Contains state-specific behavior logic
  3. Context
    • Maintains current state reference
    • Delegates requests to current state object

Benefits:

  • Reduced coupling between context and state logic
  • Single responsibility for each state class
  • Easy extension without modifying existing code

πŸ› οΈ Implementation: Water Dispenser State Machine

Here’s the complete implementation using the State Pattern:

1. State Interface

interface WaterDispenserState {
    fun handleRequest()
    fun getStateName(): String
}

2. Concrete State Classes

class HeatingState : WaterDispenserState {
    override fun handleRequest() {
        println("πŸ”₯ Heating: Water temperature is rising, please wait...")
    }
    
    override fun getStateName(): String = "Heating"
}

class CoolingState : WaterDispenserState {
    override fun handleRequest() {
        println("❄️ Cooling: Water temperature is decreasing, please wait...")
    }
    
    override fun getStateName(): String = "Cooling"
}

class StandbyState : WaterDispenserState {
    override fun handleRequest() {
        println("⏸️ Standby: Water dispenser maintains current temperature, ready to use.")
    }
    
    override fun getStateName(): String = "Standby"
}

3. Context Class

class WaterDispenser {
    private var currentState: WaterDispenserState = StandbyState()
    private var temperature: Int = 25 // Default room temperature

    fun setState(state: WaterDispenserState) {
        currentState = state
        println("πŸ”„ State Transition: ${state.getStateName()}")
    }

    fun pressButton() {
        currentState.handleRequest()
    }
    
    fun getCurrentState(): String = currentState.getStateName()
    
    fun getTemperature(): Int = temperature
}

4. Client Code

fun main() {
    val dispenser = WaterDispenser()

    // Initial state: Standby
    println("=== Water Dispenser State Machine Demo ===")
    dispenser.pressButton()

    // Switch to heating state
    dispenser.setState(HeatingState())
    dispenser.pressButton()

    // Switch to cooling state
    dispenser.setState(CoolingState())
    dispenser.pressButton()

    // Return to standby state
    dispenser.setState(StandbyState())
    dispenser.pressButton()
}

Expected Output:

=== Water Dispenser State Machine Demo ===
⏸️ Standby: Water dispenser maintains current temperature, ready to use.
πŸ”„ State Transition: Heating
πŸ”₯ Heating: Water temperature is rising, please wait...
πŸ”„ State Transition: Cooling
❄️ Cooling: Water temperature is decreasing, please wait...
πŸ”„ State Transition: Standby
⏸️ Standby: Water dispenser maintains current temperature, ready to use.

πŸ”§ Advanced Implementation: Enhanced State Machine

Let’s create a more sophisticated version with state transitions and validation:

interface State {
    fun enter()
    fun exit()
    fun handleRequest()
    fun canTransitionTo(newState: State): Boolean
}

class EnhancedWaterDispenser {
    private var currentState: State = StandbyState()
    private var temperature: Int = 25

    fun setState(newState: State) {
        if (currentState.canTransitionTo(newState)) {
            currentState.exit()
            currentState = newState
            currentState.enter()
        } else {
            println("❌ Invalid state transition from ${currentState::class.simpleName} to ${newState::class.simpleName}")
        }
    }

    fun pressButton() {
        currentState.handleRequest()
    }
}

πŸ“Š State Pattern vs Alternative Approaches

Approach Pros Cons
State Pattern βœ… Clean separation of concerns
βœ… Easy to extend
βœ… Follows OCP
❌ More classes
❌ Slight overhead
If-Else Chains βœ… Simple for few states ❌ Hard to maintain
❌ Violates OCP
Enum-Based βœ… Type-safe
βœ… Compact
❌ Mixed responsibilities
❌ Hard to extend

🎯 When to Use the State Pattern

βœ… Perfect For:

  • Complex state machines with many states
  • Objects with state-dependent behavior
  • UI components with multiple states
  • Game development (character states, AI)
  • Workflow engines and business processes

❌ Avoid When:

  • Simple state logic (use if-else instead)
  • Performance-critical applications
  • Few states with simple transitions

  • Strategy Pattern: Similar structure, but for algorithms rather than states
  • Command Pattern: Can be used together for state transitions
  • Observer Pattern: For notifying about state changes
  • Memento Pattern: For saving and restoring state

πŸ“ˆ Real-World Applications

1. Game Development

// Character states in a game
interface CharacterState {
    fun move()
    fun attack()
    fun defend()
}

class IdleState : CharacterState { /* Implementation */ }
class WalkingState : CharacterState { /* Implementation */ }
class FightingState : CharacterState { /* Implementation */ }

2. Network Connection Management

// Network connection states
interface ConnectionState {
    fun connect()
    fun disconnect()
    fun send(data: String)
}

class DisconnectedState : ConnectionState { /* Implementation */ }
class ConnectingState : ConnectionState { /* Implementation */ }
class ConnectedState : ConnectionState { /* Implementation */ }

3. Order Processing System

// E-commerce order states
interface OrderState {
    fun process()
    fun cancel()
    fun ship()
}

class PendingState : OrderState { /* Implementation */ }
class ProcessingState : OrderState { /* Implementation */ }
class ShippedState : OrderState { /* Implementation */ }

🚨 Common Pitfalls and Best Practices

1. State Transition Validation

// ❌ Avoid: No validation
fun setState(newState: State) {
    currentState = newState
}

// βœ… Prefer: Validate transitions
fun setState(newState: State) {
    if (currentState.canTransitionTo(newState)) {
        currentState = newState
    }
}

2. State Encapsulation

// ❌ Avoid: Exposing state directly
class Context {
    var state: State = IdleState()
}

// βœ… Prefer: Encapsulate state
class Context {
    private var state: State = IdleState()
    fun setState(newState: State) { /* Implementation */ }
}

3. State-Specific Data

// βœ… Good: State-specific data handling
class HeatingState : WaterDispenserState {
    private var targetTemperature: Int = 80
    
    override fun handleRequest() {
        // Use targetTemperature for heating logic
    }
}


βœ… Conclusion

Through the State Pattern, we successfully separated the water dispenser’s state logic from its core functionality, achieving the following benefits:

Key Advantages:

  • 🎯 Reduced coupling - State logic isolated from main class
  • πŸ”§ Single responsibility - Each state class focuses on its behavior
  • πŸ“ˆ Easy extension - Add new states without modifying existing code
  • πŸ›‘οΈ Better maintainability - Clear separation of concerns

Design Principles Followed:

  • Single Responsibility Principle (SRP): Each state class has one responsibility
  • Open-Closed Principle (OCP): Open for extension, closed for modification
  • Dependency Inversion Principle (DIP): Depend on abstractions, not concretions

Perfect For:

  • ATM machines (card inserted, processing, card ejected)
  • Document editors (editing, viewing, printing modes)
  • Game characters (idle, walking, fighting states)
  • Network protocols (connecting, connected, disconnected)

The State Pattern makes your code structure more flexible and is the best choice for developing state-driven applications!


πŸ’‘ Pro Tip: Combine the State Pattern with the Observer Pattern to notify other objects when state changes occur.

πŸ”” Stay Updated: Follow our Design Pattern series for more software architecture insights!




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • How to Use Multiple GitHub Accounts on One Computer: Complete SSH Setup Guide
  • Excalidraw AI: Create Professional Diagrams with Text Commands - Complete Guide
  • Complete macOS Development Environment Setup Guide for 2024
  • Design Pattern 28: Interpreter Pattern - Complete Guide with Examples
  • Design Pattern 27: Visitor Pattern - Complete Guide with Real-World IoT Examples