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
EasyImplement the State pattern in state.py for a lamp with one button. Each press moves the lamp through three states in a cycle: off, low, high, and back to off. Instead of an if chain on a string, every state is its own object that knows what happens when the button is pressed.
LampStateis the base class withpress(lamp)anddescribe().OffState,LowStateandHighStateimplement them:presscallslamp.set_state(...)with the next state (off to low, low to high, high to off) anddescribereturnsLamp is off,Lamp is on loworLamp is on high.Lampis the context: it starts inOffState,set_stateswaps the state object,press_buttondelegates to the current state, andstatusreturns the current description.
driver.py is locked: it presses the button and prints the status. Notice that Lamp never checks which state it is in; the state objects decide.
Try it yourself
from state import Lamp
# Test case handler (do not change)
test_case = input()
lamp = Lamp()
if test_case == "initial_test":
print(lamp.status())
elif test_case == "one_press_test":
lamp.press_button()
print(lamp.status())
elif test_case == "cycle_test":
for _ in range(4):
print(lamp.status())
lamp.press_button()
elif test_case == "many_presses_test":
for _ in range(7):
lamp.press_button()
print(lamp.status())
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 ClassesBorrowing SystemSearch FunctionalityAdmin InterfaceTesting and IntegrationPractice on your own: Online Python compiler