Introduction
The Observer pattern defines a one-to-many dependency between objects so that when one object (the subject) changes state, all its dependents (observers) are notified and updated automatically. It is one of the most widely used behavioral patterns because it decouples the object that generates events from the objects that react to them.
Cricket analogy: When the third umpire (subject) overturns a decision, every dependent party — the scoreboard, the broadcast commentary, and the players on the field (observers) — is automatically updated and reacts, without the umpire personally walking to inform each one.
Explanation
A subject exposes methods to subscribe (attach) and unsubscribe (detach) observers, and it keeps an internal collection of currently registered observers. Whenever the subject's state changes, it iterates over the collection and calls a notify method on each observer, typically passing the new state or an event object. Observers implement a common interface, usually a single 'update' method, so the subject can treat them polymorphically without knowing their concrete types. This is the same mechanism behind GUI event listeners, DOM 'addEventListener' calls, and publish-subscribe messaging systems: publishers do not need direct knowledge of subscribers' implementations, only that they conform to a callback contract.
Cricket analogy: The stadium's official scoreboard system lets broadcasters subscribe (attach) and unsubscribe (detach) their feeds, and whenever the score changes it loops through every registered feed calling a common 'update' method, the same way a DOM 'addEventListener' notifies subscribers without knowing their internals.
Example
from abc import ABC, abstractmethod
class Observer(ABC):
@abstractmethod
def update(self, temperature: float) -> None:
...
class WeatherStation:
"""Subject: maintains observers and notifies them of state changes."""
def __init__(self) -> None:
self._observers: list[Observer] = []
self._temperature: float = 0.0
def subscribe(self, observer: Observer) -> None:
if observer not in self._observers:
self._observers.append(observer)
def unsubscribe(self, observer: Observer) -> None:
if observer in self._observers:
self._observers.remove(observer)
def _notify(self) -> None:
for observer in self._observers:
observer.update(self._temperature)
def set_temperature(self, value: float) -> None:
self._temperature = value
self._notify()
class PhoneDisplay(Observer):
def update(self, temperature: float) -> None:
print(f"Phone display: {temperature}C")
class DashboardDisplay(Observer):
def update(self, temperature: float) -> None:
print(f"Dashboard display: {temperature}C")
station = WeatherStation()
phone = PhoneDisplay()
dashboard = DashboardDisplay()
station.subscribe(phone)
station.subscribe(dashboard)
station.set_temperature(23.5) # both observers are notified
station.unsubscribe(phone)
station.set_temperature(25.0) # only dashboard is notifiedAnalysis
The Observer pattern is classified as behavioral because it is concerned with how objects communicate and delegate responsibility for reacting to events, not with how objects are created or composed. Its main benefit is loose coupling: the subject only depends on the abstract Observer interface, so new observer types can be added without modifying the subject. The trade-off is that notification order and update frequency can become hard to reason about in large systems, and careless implementations can create memory leaks if observers are never unsubscribed. Real-world examples include DOM event listeners, GUI toolkit event handling, reactive programming libraries, and message brokers implementing publish-subscribe.
Cricket analogy: Observer is behavioral, not about how the scoreboard hardware is built, but about how the umpire communicates events to it; the loose coupling means a new observer like a fan's mobile app can subscribe without changing the umpire's code, though if an app never unsubscribes after the match, it can keep listening and drain resources needlessly.
Key Takeaways
- Subject maintains a collection of observers and exposes subscribe/unsubscribe operations.
- Notification pushes state changes to all registered observers via a common update interface.
- Decouples the event source from event handlers, enabling independent extension of both sides.
- Forms the basis of GUI event listeners and publish-subscribe messaging systems.
- Failing to unsubscribe observers can lead to memory leaks (the 'lapsed listener' problem).
Practice what you learned
1. What is the primary purpose of the Observer pattern?
2. Which category of design pattern does Observer belong to?
3. In a typical Observer implementation, what interface do all observers share?
4. What is a common real-world example of the Observer pattern?
5. What risk arises if observers are never unsubscribed from a long-lived subject?
Was this page helpful?
You May Also Like
Design Patterns Overview
An introduction to the Gang of Four design patterns and their three categories: Creational, Structural, and Behavioral.
Strategy and Command Patterns
Two behavioral patterns: Strategy swaps interchangeable algorithms at runtime; Command encapsulates a request as an executable object.
Singleton and Factory Patterns
Learn how Singleton guarantees a single instance and how Factory Method delegates object creation to a central point.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringDesign Patterns Study Notes
Software Design · 30 topics
Software EngineeringGit & Version Control Study Notes
Bash · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics