Observer pattern
Overview
The Observer pattern lets one object automatically notify other objects when something happens. Imagine a YouTube channel. When a creator uploads a video, millions of subscribers get a notification. The subscribers do not have to check the channel every five minutes. The channel (the Subject) keeps a list of subscribers (the Observers) and alerts them all at once. Here is a simple example for a news alert system: ```python class NewsAgency: def __init__(self): self.subscribers = [] def subscribe(self, person): self.subscribers.append(person) def notify_all(self, news): for person in self.subscribers: person.receive(news) ``` Now, we create subscribers and the agency. When news breaks, everyone gets it: ```python class Person: def __init__(self, name): self.name = name def receive(self, news): print(f"{self.name} received: {news}") agency = NewsAgency() agency.subscribe(Person("Rahul")) agency.subscribe(Person("Anjali")) agency.notify_all("Rain tomorrow!") ``` Both Rahul and Anjali will print the message. The `NewsAgency` does not care who they are, as long as they have a `receive` method. Let us practice. Here is a faded example. Fill in the blank to alert all the observers. ```python class GroupChat: def __init__(self): self.members = [] def send_message(self, text): for m in self.members: ____(text) # Call the member's receive method ``` The rule to remember: **The Observer pattern is a publisher keeping a list of subscribers and looping through them to send updates.** There is a sharp edge here. If you subscribe an object but forget to remove it later, the publisher keeps holding onto it forever. This is called a memory leak. If a user logs out, you must remove them from the list.
It keeps your code uncoupled. The publisher does not need to know the exact details of every subscriber. It just sends the message. You can add 100 new subscribers without changing the publisher code.
Where used: User interface buttons (clicking a button notifies the app), Sending emails when a database record changes, Real-time chat applications
Why learn this
- You will understand how event-driven systems (like clicking buttons) work.
- You will know how to separate the code that creates data from the code that displays it.
Code walkthrough
class Alarm:
def __init__(self):
self.listeners = []
def trigger(self):
for listener in self.listeners:
listener.sound()
class Siren:
def sound(self): print("WEE WOO")
class Phone:
def sound(self): print("Beep beep")
alarm = Alarm()
alarm.listeners = [Siren(), Phone()]
alarm.trigger()
Focus: for listener in self.listeners:
Aha moment
class Publisher:
def __init__(self):
self.subs = []
def alert(self):
for sub in self.subs:
sub()
def print_hello():
print("Hello!")
pub = Publisher()
pub.subs.append(print_hello)
pub.alert()
Prediction: What does this print? Look closely at what we appended to the list.
Common guess: It crashes because `print_hello` is not an object.
In Python, functions ARE objects! You do not always need a class to be an observer. You can just append a simple function to the list, and the publisher can call it.
Common mistakes
- Forgetting to unsubscribe: If an object is no longer needed but is still in the publisher's list, Python will never delete it. This wastes memory and can cause weird bugs where deleted users still receive messages.
- Slow observers blocking everything: The publisher updates observers one by one. If one observer takes 10 seconds to process the news, everyone else has to wait. Keep observer methods fast.
Recall questions
- Explain the Observer pattern in your own words, as if to a classmate.
- Why is this better than having the subscribers constantly ask "is there new data?"
- What happens if you delete a subscriber in your code but forget to remove it from the publisher's list?
Understanding checks
What does this code print?
Buying now! Buying now!
The Store has two Shoppers in its list. When `sale()` is called, it loops through the list and calls `notify()` on both of them.
Why does this code crash?
Because it tries to call the `Display` object like a function `screen(temp)` instead of calling its method `screen.show(temp)`.
The publisher must call a specific method on the observer. Objects cannot usually be called directly with parentheses unless they have special magic methods.
Practice tasks
Add a new observer
Given this code, create a new `EmailLogger` object and subscribe it to the `system` so that it also receives the error.
Continue learning
Previous: Class Creation and Instantiation