Observer Pattern — One-to-Many Dependency Notification
What You'll Learn
You will learn how the Observer Pattern establishes a subscription mechanism where multiple objects are notified when a subject changes, without the subject knowing who its dependents are. You will contrast push-based and pull-based notification strategies.
Why It Matters
UI components need to react to data changes — a stock ticker updates charts, alerts, and portfolio widgets simultaneously. Polling for changes is wasteful and slow. Observer provides a push-based notification model that keeps dependents synchronised efficiently. Instead of each widget asking "has the temperature changed?" every second, the weather station says "temperature just changed!" and all widgets react immediately.
Real-World Use
Spreadsheet cells recalculate formulas when dependent cells change. DodaTech's live dashboard subscribes observers to metrics streams — when a new metric arrives, all chart widgets update automatically. Adding a new chart type means writing one Observer and attaching it; the metrics source never needs to know about the new chart.
The Pattern
Subject maintains a list of observers and provides attach/detach methods. Observer declares the update interface. ConcreteObservers react to state changes.
from abc import ABC, abstractmethod
class Observer(ABC):
@abstractmethod
def update(self, temperature: float, humidity: float, pressure: float) -> str:
pass
class Subject(ABC):
@abstractmethod
def attach(self, observer: Observer):
pass
@abstractmethod
def detach(self, observer: Observer):
pass
@abstractmethod
def notify(self):
pass
class WeatherStation(Subject):
def __init__(self):
self._observers = []
self._temperature = 0
self._humidity = 0
self._pressure = 0
def attach(self, observer: Observer):
self._observers.append(observer)
def detach(self, observer: Observer):
self._observers.remove(observer)
def notify(self):
return [o.update(self._temperature, self._humidity, self._pressure) for o in self._observers]
def set_measurements(self, temp: float, humidity: float, pressure: float):
self._temperature = temp
self._humidity = humidity
self._pressure = pressure
return self.notify()
class DisplayWidget(Observer):
def __init__(self, name: str):
self._name = name
def update(self, temperature: float, humidity: float, pressure: float) -> str:
return f"{self._name}: {temperature}°C, {humidity}%, {pressure}hPa"
station = WeatherStation()
widget1 = DisplayWidget("Dashboard")
widget2 = DisplayWidget("Mobile App")
widget3 = DisplayWidget("Alert System")
station.attach(widget1)
station.attach(widget2)
station.attach(widget3)
for result in station.set_measurements(25.3, 60, 1013):
print(result)
print("---")
station.detach(widget2)
for result in station.set_measurements(26.1, 55, 1015):
print(result)
Dashboard: 25.3°C, 60%, 1013hPa
Mobile App: 25.3°C, 60%, 1013hPa
Alert System: 25.3°C, 60%, 1013hPa
---
Dashboard: 26.1°C, 55%, 1015hPa
Alert System: 26.1°C, 55%, 1015hPa
Structure
classDiagram
class Subject {
+attach(Observer)
+detach(Observer)
+notify()
}
class ConcreteSubject {
-state
+getState()
}
class Observer {
<>
+update()
}
class ConcreteObserverA {
+update()
}
class ConcreteObserverB {
+update()
}
ConcreteSubject --|> Subject
ConcreteObserverA ..|> Observer
ConcreteObserverB ..|> Observer
ConcreteSubject --> "*" Observer : notifies
Real-World Usage
- Java
java.util.<a href="/design-patterns/observer/">Observer</a>/Observable— legacy JDK Observer support (deprecated since Java 9 in favour ofFlow). - RxJS / ReactiveX — observables emit values to subscribers, a foundation of Reactive Programming.
- React
useEffectanduseState— components re-render when observed state changes. - Event-driven Microservices — services publish events to a message bus; consumers react without coupling to producers.
Related Patterns
- Mediator centralises communication; Observer broadcasts changes.
- Chain of Responsibility passes to one handler; Observer notifies all.
- Command encapsulates an action; Observer triggers pre-defined reactions.
- Singleton can serve as a global event bus for Observer subscriptions.
Pros and Cons
| Pros | Cons |
|---|---|
| Loose coupling between subject and observers | Observers can cause cascading update storms |
| Supports broadcast communication | Memory leaks if observers are not properly detached |
| Open/Closed — new observers added without modifying subject | Notification order is not guaranteed |
| Establishes consistency across dependent objects | Debugging complex Observer chains is difficult |
The code demonstrates the push-based variant of Observer — the subject sends the data (temperature, humidity, pressure) as parameters to the update method. The detach call demonstrates runtime subscription management: when widget2 is detached, it stops receiving updates. The subject never needs to know what the widgets do with the data — it just broadcasts.
Practice Questions
- Implement an event bus that allows subscribing to specific event types (e.g., "user.login", "data.updated").
- How would you handle an Observer that throws an exception during update — should it affect other observers?
- Compare push-based (subject sends data) with pull-based (Observer requests data) notification strategies.
- Implement an async Observer that processes updates on a separate thread to avoid blocking other observers.
Challenge
Implement a debouncing Observer that collects rapid updates and only notifies downstream observers after a quiet period. This is useful for search-as-you-type or window resize scenarios.
Real-World Task
Identify a module in your application where components poll for state changes on a timer. Refactor it to use the Observer Pattern and use DodaTech's event flow visualiser to confirm that updates propagate correctly.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro