State Pattern
Intent:
The State Pattern lets an object change how it behaves based on what state it's in. Instead of using lots of if-else statements to check the state, each state becomes its own class that knows what to do.
When to Use:
Use the State Pattern when:
- An object behaves differently depending on its state
- You have lots of if-else statements checking the state
- You want to easily add new states later
- You want to make state changes clear and easy to understand
Key Characteristics:
- Object's behavior changes based on its current state
- Each state is its own class
- State changes are clear and explicit
- No need for messy if-else statements
Think of it like a light switch - when it's ON, pressing it turns it OFF. When it's OFF, pressing it turns it ON. The behavior changes based on the current state. Instead of checking "if state is ON, do this, else do that", each state (ON and OFF) is its own class that knows what to do.
💡 Example: Simple Light Switch
Let's say we're building a light switch. The light can be in two states: ON or OFF. When you press the switch, it changes state and behaves differently based on the current state.
Without State Pattern: We would write code like "if light is ON, turn it OFF, else turn it ON". This gets messy when you have more states or more complex behavior.
With State Pattern: Each state (ON and OFF) is its own class. When you press the switch, the current state handles it and changes to the other state. This makes the code cleaner and easier to understand, especially when adding more states later.
Step 1: Define State Interface
# state_interface.py from abc import ABC, abstractmethod class LightState(ABC): @abstractmethod def press_switch(self, light): pass # Each state handles switch press differently @abstractmethod def get_state_name(self): pass # Returns current state name# state_interface.py from abc import ABC, abstractmethod class LightState(ABC): @abstractmethod def press_switch(self, light): pass # Each state handles switch press differently @abstractmethod def get_state_name(self): pass # Returns current state name
Step 2: Create On State
# on_state.py from state_interface import LightState class OnState(LightState): def press_switch(self, light): """When light is ON, pressing switch turns it OFF""" print("[State] Light is ON. Pressing switch...") print("[State] Turning light OFF") from off_state import OffState light.set_state(OffState()) # Change to OFF state def get_state_name(self): return "ON"# on_state.py from state_interface import LightState class OnState(LightState): def press_switch(self, light): """When light is ON, pressing switch turns it OFF""" print("[State] Light is ON. Pressing switch...") print("[State] Turning light OFF") from off_state import OffState light.set_state(OffState()) # Change to OFF state def get_state_name(self): return "ON"
Step 3: Create Off State
# off_state.py from state_interface import LightState class OffState(LightState): def press_switch(self, light): """When light is OFF, pressing switch turns it ON""" print("[State] Light is OFF. Pressing switch...") print("[State] Turning light ON") from on_state import OnState light.set_state(OnState()) # Change to ON state def get_state_name(self): return "OFF"# off_state.py from state_interface import LightState class OffState(LightState): def press_switch(self, light): """When light is OFF, pressing switch turns it ON""" print("[State] Light is OFF. Pressing switch...") print("[State] Turning light ON") from on_state import OnState light.set_state(OnState()) # Change to ON state def get_state_name(self): return "OFF"
Step 4: Create Light Context
# light.py from off_state import OffState class Light: def __init__(self): self.state = OffState() # Start with OFF state def set_state(self, new_state): """Change the current state""" self.state = new_state def press_switch(self): """Press the switch - behavior depends on current state""" print(f"\n[Light] Current state: {self.state.get_state_name()}") self.state.press_switch(self) # Delegate to current state print(f"[Light] New state: {self.state.get_state_name()}") def get_status(self): """Get current light status""" state_name = self.state.get_state_name() brightness = "Bright" if state_name == "ON" else "Dark" return f"Light is {state_name} ({brightness})"# light.py from off_state import OffState class Light: def __init__(self): self.state = OffState() # Start with OFF state def set_state(self, new_state): """Change the current state""" self.state = new_state def press_switch(self): """Press the switch - behavior depends on current state""" print(f"\n[Light] Current state: {self.state.get_state_name()}") self.state.press_switch(self) # Delegate to current state print(f"[Light] New state: {self.state.get_state_name()}") def get_status(self): """Get current light status""" state_name = self.state.get_state_name() brightness = "Bright" if state_name == "ON" else "Dark" return f"Light is {state_name} ({brightness})"
Step 5: Use the State Pattern (Client Code)
# main.py from light import Light # Create a light (starts in OFF state) light = Light() print("=== Initial State ===") print(light.get_status()) # Press switch multiple times print("\n=== Pressing Switch Multiple Times ===") light.press_switch() # OFF -> ON print(light.get_status()) light.press_switch() # ON -> OFF print(light.get_status()) light.press_switch() # OFF -> ON print(light.get_status()) light.press_switch() # ON -> OFF print(light.get_status())# main.py from light import Light # Create a light (starts in OFF state) light = Light() print("=== Initial State ===") print(light.get_status()) # Press switch multiple times print("\n=== Pressing Switch Multiple Times ===") light.press_switch() # OFF -> ON print(light.get_status()) light.press_switch() # ON -> OFF print(light.get_status()) light.press_switch() # OFF -> ON print(light.get_status()) light.press_switch() # ON -> OFF print(light.get_status())
Output:
=== Initial State === Light is OFF (Dark) === Pressing Switch Multiple Times === [Light] Current state: OFF [State] Light is OFF. Pressing switch... [State] Turning light ON [Light] New state: ON Light is ON (Bright) [Light] Current state: ON [State] Light is ON. Pressing switch... [State] Turning light OFF [Light] New state: OFF Light is OFF (Dark) [Light] Current state: OFF [State] Light is OFF. Pressing switch... [State] Turning light ON [Light] New state: ON Light is ON (Bright) [Light] Current state: ON [State] Light is ON. Pressing switch... [State] Turning light OFF [Light] New state: OFF Light is OFF (Dark)
✅ Benefits
1. Easy to Understand:
- Each state is its own class, so it's clear what each state does
- Easy to read and maintain the code
2. No Messy If-Else:
- Don't need lots of if-else statements checking the state
- Each state class handles its own behavior
3. Easy to Add More States:
- Want a new state? Just create a new state class
- Don't need to change existing code
4. Clear State Changes:
- It's obvious when and how states change
- Easy to see what's happening and fix problems
🎯 Key Points
- State Pattern lets an object behave differently based on its current state
- Each state is its own class
- The object asks the current state to handle actions
- State changes are clear and easy to see
- No need for messy if-else statements checking the state
🌟 Quick Summary
- Problem: An object behaves differently based on its state, and using lots of if-else statements makes the code messy and hard to read
- Solution: Create a separate class for each state. The object asks the current state to handle actions, and the state knows what to do
- Benefit: Clean, easy-to-read code. Easy to add new states without changing old code. State changes are clear
- Example: Light switch - ON state and OFF state are separate classes, each knowing what happens when you press the switch
🎉 End
The State Pattern helps you manage objects that behave differently based on their state. It's perfect when you have objects that can be in different states (like ON/OFF, OPEN/CLOSED, PLAYING/PAUSED) and each state needs to behave differently!