Behavioral Pattern
Observer
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Subscribe and get notified.
The Observer pattern is like subscribing to a newsletter: many subscribers (observers) register with a publisher (subject), and whenever the publisher has news, it automatically notifies everyone. The subject doesn't need to know who the subscribers are — just that they want updates.
bad.py
# Problem: subject hard-codes who to notify — closed to new listeners. class Order: def complete(self): self.status = "done" EmailService().send(self.user, "done") Analytics().track("order_done") # Inventory? Slack? Edit this method every time.
good.py
# Fix: Observer — subject notifies a list of subscribers. class Order: def __init__(self): self._observers = [] def subscribe(self, observer): self._observers.append(observer) def complete(self): self.status = "done" for obs in self._observers: obs.on_complete(self) # don't care who they are class EmailNotifier: def on_complete(self, order): send_email(order.user, "done") class AnalyticsListener: def on_complete(self, order): track("order_done") order = Order() order.subscribe(EmailNotifier()) order.subscribe(AnalyticsListener()) order.complete() # both get notified
Subject and observers.
Participants: a Subject that maintains a list of observers and offers
subscribe/unsubscribe/notify, and Observer
objects with an update() method the subject calls on change.
- Push vs pull — the subject can push data in the notification, or observers can pull what they need from the subject afterward.
- Loose coupling — the subject knows observers only through an interface.
- Dynamic — observers can come and go at runtime.
Pitfalls and modern forms.
- Lapsed listener / memory leaks — observers that forget to unsubscribe are kept alive by the subject; use weak references or explicit teardown.
- Ordering & cascades — notification order is usually undefined, and updates can trigger further updates (cycles), risking storms or infinite loops.
- Thread safety — concurrent subscribe/notify needs care; a snapshot of the listener list is common.
- Modern incarnations — event listeners, pub/sub, reactive streams
(RxJS/Reactor), and the deprecated
java.util.Observer; reactive libraries add backpressure and composition on top of the basic idea.