The Observer Pattern and the Hollywood Principle
When a Shelfwise order ships, mark_shipped() emails the customer, adds loyalty points and records an analytics event. Each of those is a direct call inside Order, and every new request ("also notify the warehouse dashboard") adds another line to a class that should only know about orders. Observer turns this inside out.
The rule
Don't call us, we'll call you: subscribers wait, the subject announces.
That line is the Hollywood Principle: lower-level pieces do not keep calling in to ask "has anything happened?"; the higher-level piece calls them when something does. Observer is its most familiar pattern.
It has two roles:
- Subject: keeps a list of subscribers and notifies them when an event happens.
- Observers: register with the subject and offer one method for it to call.
class Order:
def __init__(self, lines):
self.lines = lines
self.status = "paid"
self._shipped_listeners = []
def on_shipped(self, listener):
self._shipped_listeners.append(listener)
def mark_shipped(self):
self.status = "shipped"
for listener in self._shipped_listeners:
listener.order_shipped(self)
order.on_shipped(receipt_mailer)
order.on_shipped(loyalty_service)
order.on_shipped(analytics)Order no longer knows what a mailer or a loyalty service is. It knows only that listeners have an order_shipped method. A new feature subscribes itself, and Order never changes: Open/Closed again, this time through an event.
Observers are loosely coupled to the subject. You meet this shape everywhere: button click handlers in a user interface, signals in web frameworks, and, at a much larger scale, publish-subscribe messaging between services.
Two costs come with it. The flow of control is harder to follow, because the subject no longer shows who reacts. And the order in which observers run should not matter; if it does, the design needs a rethink.
The shortcut
Look for a method that finishes its own job and then calls a growing list of unrelated services. That list is a set of observers waiting to be born. The method keeps its own job and ends with one "notify".
Spot the growing list
Book.reserve()updates stock, then calls the restock planner, the "only 2 left" banner and the supplier emailer.
The trap is an observer that changes the subject in a way that fires the same event again, creating a loop that never ends.
Drills
Answer fast — trust the rule.
What does the Hollywood Principle say?
In Observer, what does the subject need to know about its observers?
Shelfwise adds a warehouse dashboard that must react when orders ship. With Observer, what changes in Order?
What is a real cost of the Observer pattern?
Remember
- Don't call us, we'll call you: subscribers wait, the subject announces.
- A method that ends by calling a growing list of unrelated services wants to notify observers instead.
- Trap: observers that re-trigger the event they react to, or that depend on running order.
