Card 01/ 09
Observer Pattern: Notifying Many Objects When One Changes
An order is placed, and the email, the stock and the ledger all hear about it without the order knowing them.
The Observer pattern lets any number of objects subscribe to another object and be called back when it changes. The object being watched keeps a list of its subscribers and calls each one, so it never needs to know what they are or what they do. This card covers the problem that calls for it, the intent, and when it fits.
The problem is an object that calls every dependant by name. An order service, once an order is placed, must send a confirmation email, reduce the stock, and add the sale to the ledger. Written directly, the service imports all three, and adding a fourth reaction, such as a loyalty points update, means editing the service.
Without the patternthe service knows every reaction
def place(self, order):
self.email.send_confirmation(order)
self.stock.reduce(order.items)
self.ledger.record(order.total_pence)Every new reaction is another line in place, and another class the service depends on.
With the patternthe service knows a list
def place(self, order):
for observer in self._observers:
observer.order_placed(order)Each reaction subscribes itself, and place is never edited to add one.
The Gang of Four state the intent as: define a one-to-many dependency between objects so that when one object changes state, all its dependants are notified and updated automatically. The object being watched is the subject, also called the publisher; those watching are its observers, also called subscribers or listeners.
A newsletter is a familiar picture: readers subscribe, every issue reaches all of them, and the publisher does not need to know who they are. The picture breaks on forgetting. A newsletter you forgot to cancel can be ignored, but an observer that was never unsubscribed still runs its code on every notification, and stays in memory.
Observer is one of eleven behavioural patterns, the family concerned with which object does which part of the work, and how objects communicate.
| Pattern | What it does |
|---|---|
| Chain of Responsibility | Passes a request along a chain of handlers until one handles it. |
| Command | Turns a request into an object that can be stored, queued or undone. |
| Interpreter | Represents a small language's grammar as classes and evaluates its sentences. |
| Iterator | Visits the elements of a collection without exposing how it is stored. |
| Mediator | Routes the communication between objects through one central object. |
| Memento | Captures an object's state so it can be restored later. |
| Observer (this topic) | Notifies many objects when one object changes. |
| State | Changes an object's behaviour when its internal state changes. |
| Strategy | Swaps one algorithm for another behind a common interface. |
| Template Method | Fixes an algorithm's steps in a base class and lets subclasses fill some in. |
| Visitor | Adds operations to a class hierarchy without changing its classes. |
Use it when
- A change in one object must reach others, and which others is decided elsewhere or changes at run time.
- The object that changes should not depend on the classes that react.
- Reactions are added and removed over the program's life, such as windows that open and close.
Avoid it when
- There is one dependant and there will only be one, so a direct call is clearer.
- The order in which reactions run matters, because observers are usually called in an order nobody should rely on.
- A chain of observers that notify other subjects makes it hard to see what one change sets off.
← → move through sections and cards