Observer is a behavioral design pattern that let you define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Imagine that you have two objects, a
Customer and a
Store. The store is about to receive a large shipment of a new product, which is very interesting to some customers.
While customers could visit the store every day to check the availability of product, most of these trips would be pointless while the product is still on the way.
On the other hands, the store could send tons of emails (spam) to all customers each time it receives a new shipment. But this would upset other customers, that don't care about the new product.
Thus, we have a conflict: either customer wastes resources on periodic checks or the store itself wastes resources notifying wrong customers.
Let's call an object that has some interesting state a
Publisher. Let's call another object that wants to track changes to that state a
The Observer pattern provides the publisher with a list of the subscribers, interested in tracking its state. The list can be modified via several subscription methods available to subscribers. Thus, each subscriber is able to add or remove itself from the list whenever it wants.
Now the interesting part. Each time there's an interesting event inside the publisher, it goes over its subscriber list and calls notification method in each of their objects.
Publishers can work with any subscriber since all of them follows the common interface. This interface defines a notification method with a set of parameters, which can be used to supply subscribers with the event details.
If an application has several publisher types, the common publisher interface also can be extracted. It would consist of several subscription methods. The interface would allow subscribers to observe publishers' state without coupling to their concrete classes.
Once subscribed to a newspaper or magazine, you no longer need to go to the store and check if a next issue is available. Instead, the publisher will send new issues directly to your mailbox right after the publication.
The publisher maintains a list of subscribers and knows which magazines they're interested in. Subscribers can leave the list at any time when they wish to stop publisher sending new magazine issues to them.
Publisher issues events interesting for other objects. These events occur when the publisher changes its state or when it executes some behaviors. Publishers contain a subscription infrastructure that allows new subscribers to join and old subscribers to leave the list.
When a new event happens, publisher goes over the list of subscribers and calls their notification method, defined by the Subscriber interface.
Subscriber defines the notification interface. In most cases, it consists of a single
updatemethod. The method may have several parameters that allow subscribers receiving some of the event details along the update.
Concrete subscriber implements the notification interface and performs some action in response to the update issued by the Publisher.
Subscribers usually need more that just a simple method call to properly handle the update. For this reason, publishers often pass some context data as arguments of the notification method. This might even be a reference to the publisher's object itself.
Client creates publisher and subscriber objects separately and then registers subscribers for publisher updates.
Sometimes it's convenient to have direct access from a subscriber's object to a specific publisher. The link is often established via subscriber's constructor. It allows the subscriber to fetch updated state directly from the publisher object upon receiving a notification.
In this example, the Observer allows the text editor object to notify other objects about changes in its state. The list of subscribers is compiled dynamically. Objects can both start and stop listening for the updates in the runtime.
In this implementation, the editor does not maintain the list of subscribers by itself but rather delegates this job to a special helper object. This allows other objects to reuse the subscription infrastructure. Thus, the Observer pattern allows dynamic configuration of the handlers for various events that occur inside objects.
Adding new subscribers to the program does not require changes to existing publisher classes, as long as they work with subscribers through a common interface.
// Base publisher class. It should include the subscription management code and // notification methods. class EventManager is field listeners: hash map of eventTypes and EventListeners method subscribe(eventType, listener) is listeners.add(eventType, listener) method unsubscribe(eventType, listener) is listeners.remove(eventType, listener) method notify(eventType, a) is foreach listeners.of(eventType) as listener listener.update(a) // Concrete publisher, which contains real business logic interesting for some // subscribers. We could extend this class from a base publisher, but that is // not always possible in real life, since a publisher it might already have a // parent class. In this case, you can patch the subscription logic in with // composition, just like we did it here. class Editor is field events: EventManager field file: File constructor Editor() is events = new EventManager() // Business logic methods can notify subscribers about the changes. method openFile(filename) is this.file = new File(filename) events.notify('open', filename) method saveFile() is file.write() events.notify('save', file.name) // ... // Common subscribers interface. By the way, modern programming languages allow // to simplify this code and use functions as subscribers. interface EventListener is method update(a) // List of concrete listeners. They react to publisher updates by doing some // useful work. class LogOpenListener is field log: File constructor LogOpenListener(filename) is this.log = new File(filename) method update(filename) is log.write("Opened: " + filename) class EmailNotificationListener is field email: string constructor EmailNotificationListener(email) is this.email = email method update(filename) is system.email(email, "Someone has changed the file: " + filename) // Application can configure publishers and subscribers even in run time. class Application is method config() is editor = new TextEditor() editor.events.subscribe("open", new LogOpenListener("/path/to/log/file.txt")) editor.events.subscribe("save", new EmailNotificationListener("email@example.com"))
When changes to the state of one object may require changing other objects, but the are unknown beforehand or change dynamically.
For example, you are developing a GUI framework focused on buttons. You want your clients to be able to hook some custom code to your buttons so that it will fire whenever users press the button.
The Observer pattern allows any object that implements the subscriber interface to subscribe for the event notifications in publisher objects.
Some objects should observe the others, but only for a limited time or in specific cases.
The Observer pattern lets publishers maintain dynamic lists of subscribers. All subscribers can join and leave the list at will, in runtime.
How to Implement
Differentiate between the core (or independent) functionality and the optional (or dependent) functionality. The former will act as a publisher, and the later will serve as subscribers.
Create the Subscriber interface. In most cases, a single
updatemethod is enough.
Create the Publisher interface and describe the operations for starting and terminating subscription in it. Remember that publisher should work with subscribers via their common interface.
You have to decide where you put the actual subscription list and the implementation of the subscribtion methods. Usually, this code looks the same for all types of publishers. So the obvious place to put it is a base abstract class derived directly from a Publisher interface. But if you are integrating Observer to an existing class hierarchy, it might be more convenient to create a small helper class that will be maintaining the subscription for specific publishers.
Create Concrete Publisher classes. They should send notifications to the whole list of subscribers each time when something important happens inside the object.
updatemethods in Concrete Subscribers. Most subscribers would need some context data about the event. It can be passed as an argument to the
updatemethod. But there's another option. Upon receiving a notification, the subscriber can fetch any data directly from the publisher object. In this case, the publisher must pass itself via the update method. The less flexible option is to link a publisher to the subscriber permanently via the constructor.
The Client code must create all necessary subscribers and register them with proper publishers.
Pros and Cons
- Publisher is not coupled to concrete subscriber classes.
- You can subscribe and unsubscribe objects dynamically.
- Follows the Open/Closed Principle.
- Subscribers are notified in random order.
Relations with Other Patterns
Chain of Responsibility passes a request sequentially along a dynamic chain of potential receivers until one of them handles a request.
Observer passes a request to all concerned receivers at the same time but allows them to subscribe and unsubscribe from receiving further requests dynamically.
Command establishes a one-directional connection from senders to receivers.
Mediator has senders and receivers reference each other indirectly.
The main goal of the Mediator pattern is to elimintate mutual dependencies between a set of system components. All components become dependent from a single mediator object instead. On the other hand, the goal of the Observer is to establish dynamic one-way connections between objects, where some objects act as subordinates of others.
There's a quite popular Mediator implementation that relies on the Observer pattern. The mediator object acts as publisher and all of the collegue components become subscribers. They dynamically subscribe and unsubscribe to the mediator's events. Implemented this way, both patterns may look very similar.
But the Mediator pattern can be implemented in different way, where all the components are permanently linked to the same mediator object. This implementation won't look as Observer but still be a case of the Mediator pattern.
Now imagine another program where all the components become publishers and allow dynamic connections with each other. There won't be a central mediator object, but rather distribute system of observers.