State Pattern
Part of the Object Oriented Programming section of Coddy's Python journey — lesson 53 of 64.
The State Pattern allows an object to change its behavior when its internal state changes. The object appears to change its class based on its current state.
Here are simple state classes for a traffic light:
class RedState:
def next_state(self, light):
print("Red -> Green")
light.state = GreenState()
def current_color(self):
return "Red"
class GreenState:
def next_state(self, light):
print("Green -> Yellow")
light.state = YellowState()
def current_color(self):
return "Green"
class YellowState:
def next_state(self, light):
print("Yellow -> Red")
light.state = RedState()
def current_color(self):
return "Yellow"Each state defines what happens when transitioning to the next state.
Create a context class that holds the current state:
class TrafficLight:
def __init__(self):
self.state = RedState() # Start with red
def change(self):
self.state.next_state(self)
def get_color(self):
return self.state.current_color()The traffic light delegates behavior to its current state object.
Use the traffic light:
light = TrafficLight()
print(f"Current: {light.get_color()}")
light.change() # Red -> Green
print(f"Current: {light.get_color()}")
light.change() # Green -> Yellow
print(f"Current: {light.get_color()}")
light.change() # Yellow -> Red
print(f"Current: {light.get_color()}")Create another example with a simple player:
class PlayingState:
def play(self, player):
print("Already playing")
def stop(self, player):
print("Stopping music")
player.state = StoppedState()
class StoppedState:
def play(self, player):
print("Starting music")
player.state = PlayingState()
def stop(self, player):
print("Already stopped")
class MusicPlayer:
def __init__(self):
self.state = StoppedState()
def play(self):
self.state.play(self)
def stop(self):
self.state.stop(self)
player = MusicPlayer()
player.play() # Starting music
player.play() # Already playing
player.stop() # Stopping music
player.stop() # Already stoppedOutput:
Current: Red
Red -> Green
Current: Green
Green -> Yellow
Current: Yellow
Yellow -> Red
Current: Red
Starting music
Already playing
Stopping music
Already stoppedKey Point: The State Pattern encapsulates state-specific behavior in separate classes and lets the context object delegate to the current state. When the state changes, the behavior changes automatically. This eliminates complex if/else statements and makes adding new states easier.
Challenge
EasyIn this challenge, you'll implement the State class in state.py with proper encapsulation and methods. The file contains detailed TODO comments to guide your implementation step-by-step.
The test suite is designed to verify your implementation meets all requirements and handles exceptional conditions correctly. Use the test results to understand expected behavior and refine your solution.
Cheat sheet
The State Pattern allows an object to change its behavior when its internal state changes. The object appears to change its class based on its current state.
Create state classes that define behavior for each state:
class RedState:
def next_state(self, light):
print("Red -> Green")
light.state = GreenState()
def current_color(self):
return "Red"
class GreenState:
def next_state(self, light):
print("Green -> Yellow")
light.state = YellowState()
def current_color(self):
return "Green"Create a context class that holds the current state:
class TrafficLight:
def __init__(self):
self.state = RedState() # Start with red
def change(self):
self.state.next_state(self)
def get_color(self):
return self.state.current_color()Usage example:
light = TrafficLight()
print(f"Current: {light.get_color()}") # Current: Red
light.change() # Red -> Green
print(f"Current: {light.get_color()}") # Current: GreenThe State Pattern encapsulates state-specific behavior in separate classes and lets the context object delegate to the current state. This eliminates complex if/else statements and makes adding new states easier.
Try it yourself
from state import State
# Comprehensive test case handler
test_case = input()
if test_case == "basic_test":
obj = State("Test Name")
obj.display_info()
elif test_case == "validation_test":
obj = State("Validation Test")
print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
obj = State("")
obj.display_info()
print(f"Name property: {obj.name}")
elif test_case == "special_chars_test":
obj = State("Test@#$%")
obj.display_info()
print(f"Name property: {obj.name}")
elif test_case == "long_name_test":
long_name = "A" * 100
obj = State(long_name)
obj.display_info()
print(f"Name length: {len(obj.name)}")
elif test_case == "multiple_states_test":
states = [
State("California"),
State("Texas"),
State("New York"),
State("Florida")
]
for state in states:
state.display_info()
print(f"Name via property: {state.name}")
elif test_case == "attribute_access_test":
obj = State("Access Test")
# Direct access to private attribute (not recommended)
print(f"Direct access (not recommended): {obj._name}")
# Access via property (recommended)
print(f"Property access (recommended): {obj.name}")
elif test_case == "property_behavior_test":
obj = State("Original Name")
print(f"Original name: {obj.name}")
# Try to modify the name (will fail as there's no setter)
try:
obj.name = "New Name"
except AttributeError as e:
print(f"Error when trying to modify name: {e}")
# Show name is unchanged
print(f"Name after modification attempt: {obj.name}")This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self ParameterMethodsAttributesConstructor Method (__init__)Recap - Simple Calculator4Inheritance
Basic InheritanceThe super() FunctionMethod OverridingMultiple InheritanceMethod Resolution OrderRecap - Employee Hierarchy7Special Methods
Magic Methods IntroductionOperator OverloadingContainer Magic MethodsRecap - Custom List10Design Patterns Part 1
Intro to design patternSingleton PatternFactory PatternObserver PatternStrategy Pattern2Decorators
Introduction to DecoratorsProperty DecoratorStatic Method DecoratorClass Method Decorator5Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceMixinsStatic and Class MethodsClass DecoratorsContext Managers11Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite Pattern3Class Properties
Instance vs Class VariablesProperty DecoratorsPrivate AttributesRecap - Bank Account Manager6Encapsulation
Public, Protected, Private MemAccess ModifiersInformation HidingProperty Decorators AdvancedRecap - Student Records System12Project: Library Management
Project OverviewBook and User Classes