As we have explored Structural Patterns and Creational Patterns, let's now dive into Behavioral Patterns. As the name suggests, these patterns define how code behaves under certain circumstances.
In this article, we’ll break down some essential behavioral patterns with real-world examples and Python code snippets to help you understand how they work.
1. State Pattern
The State Pattern allows an object to change its behavior when its internal state changes.
Example: Light Bulb
Imagine a light bulb in your home:
By default, it is turned off.
When you turn on the switch, it gets turned on.
Based on its state (ON/OFF), it behaves differently.
When OFF, it does nothing.
When ON, it emits light.
This is an example of the State Pattern, where an object's behavior depends on its internal state.
Python Code Example:
class LightBulb:
def __init__(self):
self.state = OffState() # Initial state is OFF
def set_state(self, state):
self.state = state
def toggle(self):
self.state.handle(self)
class State:
def handle(self, bulb):
pass
class OnState(State):
def handle(self, bulb):
print("The light is already ON.")
bulb.set_state(OffState()) # Switching to OFF state
class OffState(State):
def handle(self, bulb):
print("Turning the light ON.")
bulb.set_state(OnState()) # Switching to ON state
# Usage
bulb = LightBulb()
bulb.toggle() # Turns ON
bulb.toggle() # Turns OFF
2. Iterator Pattern
The Iterator Pattern is used to traverse a collection without exposing its underlying structure.
Example: Scrolling Through Friends List on Instagram
When you scroll through your friends list on Instagram, you:
Move from one friend to another without worrying about how Instagram fetches the data.
Just iterate through the list without needing to understand its internal implementation.
Similarly, when you use a for loop in Python, you iterate through elements without knowing how they are stored in memory.
Python Code Example:
class FriendsList:
def __init__(self, friends):
self.friends = friends
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.friends):
friend = self.friends[self.index]
self.index += 1
return friend
else:
raise StopIteration
# Usage
friends = FriendsList(["Alice", "Bob", "Charlie"])
for friend in friends:
print(friend)
3. Strategy Pattern
The Strategy Pattern allows selecting different algorithms (or methods) at runtime based on the situation.
Example: Choosing a Mode of Transportation
If you want to travel from Point A to Point B, you have multiple options:
Cycle
Bus
Train
Car
Each mode of transport achieves the same goal but in different ways. This is what the Strategy Pattern is all about—choosing the best method dynamically.
Python Code Example:
class TravelStrategy:
def travel(self):
pass
class Cycle(TravelStrategy):
def travel(self):
print("Traveling by Cycle.")
class Bus(TravelStrategy):
def travel(self):
print("Traveling by Bus.")
class Train(TravelStrategy):
def travel(self):
print("Traveling by Train.")
# Usage
def travel_method(strategy: TravelStrategy):
strategy.travel()
travel_method(Cycle()) # Traveling by Cycle
travel_method(Bus()) # Traveling by Bus
travel_method(Train()) # Traveling by Train
4. Observer Pattern
The Observer Pattern establishes a relationship where multiple objects depend on a single object and get notified when it changes.
Example: A Teacher and Students in a Classroom
In a classroom:
The teacher speaks, and students listen.
If the teacher changes the topic, students automatically adjust to the new topic.
Students are the observers, and the teacher is the subject.
Whenever the teacher (subject) changes the state, all the students (observers) are notified.
Python Code Example:
class Teacher:
def __init__(self):
self.students = []
def register(self, student):
self.students.append(student)
def notify(self, message):
for student in self.students:
student.update(message)
class Student:
def __init__(self, name):
self.name = name
def update(self, message):
print(f"{self.name} received message: {message}")
# Usage
teacher = Teacher()
student1 = Student("Alice")
student2 = Student("Bob")
teacher.register(student1)
teacher.register(student2)
teacher.notify("Today's lesson is about Design Patterns.")
Conclusion
By understanding these behavioral patterns, you can write more maintainable and scalable code.
State Pattern: Objects behave differently based on their state.
Iterator Pattern: Iterating through a collection without knowing its structure.
Strategy Pattern: Using different methods to achieve the same goal.
Observer Pattern: When one object changes, dependent objects are notified.
These patterns enhance code flexibility and make your software easier to manage. Try implementing them in your own projects!