Other names: Event-Subscriber, Listener
Observer is a behavioral design pattern (hereinafter referred to as DP) that defines a subscription mechanism that notifies multiple objects of events occurring to the object they are observing. (observe).
Imagine that you have two types of audiences:
Khách hàng and
Cửa hàng . The customer is very interested in a particular brand of product (say a new iPhone model) going on sale in the store.
Customers can visit the store every day to check if the product is available. But while the product is yet to be released, most trips to this store would be meaningless.
On the other hand, the store may send tons of emails (which customers think is spam) to all customers every time a new product is available. This will save some customers from going to the store countless times, however, at the same time, it will annoy other customers who are not interested in the new product.
Looks like there’s a conflict going on here. Either customers waste time checking to see if a product is in stock, or the store wastes resources by notifying customers who don’t want to receive notifications.
The object that has some state that other object is interested in is usually called subject , but since it will also notify other objects of changes to its state, we’ll call it publisher. . All other audiences that want to track changes to the publisher ‘s status are called subscribers .
Observer DP suggests that we should add subscriptions to the publisher class so that individual objects can subscribe or unsubscribe from the event stream coming from that publisher. It sounds complicated, but the reality is simpler than you thinkIn practice, this mechanism involves 1) an array for storing the list of references to subscriber objects, and 2) several public methods that allow adding and deleting subscribers.
Now, whenever a critical event happens to the publisher, the publisher runs through the subscriber array and calls the specific notification method of those objects.
Real applications can have dozens of different subscriber classes interested in keeping track of the events of the same publisher class. In this case, we shouldn’t couple publisher into all of those classes. Besides, we may not even know about some of those classes in advance if our publisher class is written for the purpose of being used by others.
That’s why it’s important that all subscribers implement the same interface, and the publisher only communicates with subscribers over that interface. This interface should declare the message method along with a set of parameters that the publisher can use to pass some contextual data along with the message.
If your application has many different types of publishers and you want to make the subscriber compatible with all publishers, you can make all publishers follow the same interface. This interface only needs to describe some of the subscribe methods. The interface will allow the subscriber to observe the publisher’s state without couple to specific publisher classes.
For example with reality
If you subscribe to a newspaper or magazine, you don’t have to go to the store to check if the next issue is available. Instead, the publisher sends new issues directly to your inbox immediately after publication or even before.
Publishers keep a list of subscribers and know which journals they are interested in. Subscribers can leave the list at any time when they no longer want the publisher to send them new issue issues.
- The publisher fires events that are of interest to other objects. These events occur when the publisher changes state or performs some action. Publisher has a subscribe mechanism that allows objects to go in / out of a subscriber list.
- When a new event occurs, the publisher traverses the subscriber list and calls the notification method declared in the subscriber interface of each subscriber object.
- The Subscriber interface declares the notification interface. In most cases, it includes only one method,
update. The method can have several parameters that allow the publisher to pass some event details along with the update event.
- The specific Subscriber class takes some action in response to messages issued by the publisher. All of these classes must implement the same interface so that the publisher is not couple with specific classes.
- Usually, the subscriber needs some contextual information to properly handle the update event. For this reason, the publisher usually passes some context data as the param of the message method. The publisher can pass itself as a parameter, allowing the subscriber to directly fetch any data it needs.
- The client creates separate publisher and subscriber objects and then subscribes to the publisher to listen for publisher updates.
Dynamically compiled subscriber lists: Objects can start or stop listening for notifications at runtime, depending on the desired behavior of your application.
In this implementation, the class editor does not automatically save the subscribe list. It delegates this work to a special helper object specifically for that. You can upgrade that object to turn it into a centralized event dispatcher, allowing any object to act as a publisher.
Adding new subscribers to the program does not require changes to existing publisher classes, as long as they work with all subscribers through the same interface.
// The base publisher class includes subscription management
// code and notification methods.
class EventManager is
private field listeners: hash map of event types and listeners
method subscribe(eventType, listener) is
method unsubscribe(eventType, listener) is
method notify(eventType, data) is
foreach (listener in listeners.of(eventType)) do
// The concrete publisher contains real business logic that's
// interesting for some subscribers. We could derive this class
// from the base publisher, but that isn't always possible in
// real life because the concrete publisher might already be a
// subclass. In this case, you can patch the subscription logic
// in with composition, as we did here.
class Editor is
public field events: EventManager
private field file: File
constructor Editor() is
events = new EventManager()
// Methods of business logic can notify subscribers about
method openFile(path) is
this.file = new File(path)
method saveFile() is
// Here's the subscriber interface. If your programming language
// supports functional types, you can replace the whole
// subscriber hierarchy with a set of functions.
interface EventListener is
// Concrete subscribers react to updates issued by the publisher
// they are attached to.
class LoggingListener implements EventListener is
private field log: File
private field message
constructor LoggingListener(log_filename, message) is
this.log = new File(log_filename)
this.message = message
method update(filename) is
class EmailAlertsListener implements EventListener is
private field email: string
constructor EmailAlertsListener(email, message) is
this.email = email
this.message = message
method update(filename) is
// An application can configure publishers and subscribers at
class Application is
method config() is
editor = new Editor()
logger = new LoggingListener(
"Someone has opened the file: %s")
emailAlerts = new EmailAlertsListener(
" <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ",
"Someone has changed the file: %s")
*** Use the Observer pattern when changes to an object’s state may require the alteration of other objects and the actual object list is not known or dynamically changed. * * You can often experience this problem when working with GUI classes. Example: You have created custom button classes and you want to allow the client to connect some custom code to those buttons so that the code is activated whenever the user clicks on a button. Observer DP allows any object to implement a subscriber subscribe interface to receive event notifications from the publisher object. You can add subscriptions to your nodes, allowing clients to connect to their custom code through custom subscriber classes.
*** Use this DP when some objects in your app have to observe others, but only for a limited time or under specific circumstances. ** The subscribe list is dynamic, so the subscriber can enter or exit the list whenever it needs to.
How to implement
- Look at your business logic and try to divide it into two parts: core functionality, independent of other code -> this will be the publisher; the rest will turn into a set of subscriber classes.
- Declare the subscriber interface. At a minimum, it must declare an
- Declare the publisher interface and describe two methods: adding a subscriber object and removing that object from the list. Remember that publishers can only work with subscribers through the subscriber interface.
- Decide where to place the actual subscribe list and implementations of the subscribe methods. Usually this code will be the same for all publisher types, so the obvious place to put it is in an inherit abstraction class directly from the publisher interface. The publisher specific class extends that class, inheriting the subscribe behavior. However, if you are applying this DP to the existing class system, consider the composition-based approach: placing the subscribe logic on a separate object and getting all real publishers to use it.
- Create publisher specific classes. Every time something important happens within the publisher, the publisher has to notify all of his subscribers.
- Implement methods of update notification in specific subscriber classes. Most subscribers will need some contextual data about the event. That data can be passed as a param of the notification method. But there is another option, which is that when the subscriber receives the message, the subscriber can pull any data directly from the message. In this case, the publisher must pass itself through the update method. The less flexible option is to link the publisher with the perpetual subscriber through an initiation method.
- The client must create all necessary subscribers and subscribe to them with the appropriate publishers.
Advantages and disadvantages
Pros 1: Open / Closed Principle: You can add new subscriber classes without having to change the code of the publisher class (and vice versa if there is a publisher interface). Pros 2: You can establish relationships between objects at runtime. Cons: Not in control of the order in which the subscriber receives the message.
Relationship with other DPs
- The Chain of Responsibility, Command, Mediator, and Observer are different solutions to the problem of connecting the sender and receiver of the request: The Chain of Responsibility passes a sequence request along a dynamic chain of potential recipients for until one of them processes the request. Command establishes a one-way connection between sender and receiver. Mediator eliminates direct connections between senders and receivers, forcing them to communicate indirectly through an intermediary. The Observer allows the recipient to dynamically subscribe and unsubscribe from the request.
- The difference between Mediator and Observer is usually not great in many cases. In most cases, you can implement one of these DPs; but sometimes you can do both. Let’s see how we do that.
The Mediator’s main goal is to eliminate interdependencies among a set of system components. Instead, these components become dependent on a single intermediate object. The Observer’s goal is to establish one-way dynamic connections between objects, where some objects act as subordinates of others.
There is a popular Mediator implementation that is based on the Observer. The mediator object acts as the publisher, and the components act as subscribers subscribe and unsubscribe from the mediator events. When Mediator is implemented in this way, it may look very similar to Observer.
When you find it confusing, remember that you can implement Mediator in other ways. For example, you can permanently associate all components with the same Mediator object. This implementation will not be the same as the Observer but will still be a Mediator version.
Now imagine a program where all the components have become publishers, allowing dynamic connections between each other. There will be no centralized mediator object, only one distributed observer group.