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:
- High Coupling
- State logic mixed with water dispenser core functionality
- Difficult to maintain and modify state behavior
- Single Responsibility Principle Violation
- Water dispenser class handles both state logic and core functionality
- Class becomes overloaded with responsibilities
- 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:
- State Interface
- Defines common interface for all states
- Ensures consistent behavior across states
- Concrete States
- Each state implements the interface
- Contains state-specific behavior logic
- 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
π Related Design Patterns
- 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
}
}
π Related Articles
- Design Pattern 1: Object-Oriented Concepts
- Design Pattern 2: Design Principles
- Strategy Pattern
- Command Pattern
- Observer Pattern
β 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: