In the previous post, we discussed the benefits of prototype patterns, why they can be helpful and when can it create problems.
Observer design pattern
The observer is a design pattern in which when one part of the application changes and other parts are needed to be updated. If the subscriber object is changed/modified, it broadcasts its changes to dependent objects. This is an example of event-driven programming in JavaScript. It facilitates object-oriented design. Another for the Observer pattern is pub/sub, short for Publication/Subscription.
However, Publish/Subscribe patterns use a topic/event channel that sits in between participants to receive notifications and the object firing the event.
Diagram
Participants
The objects participating in these patterns are:
- Observer Subject
- Maintains a list of observers
- Interface for
observers
to subscribe or unsubscribe - Notify
observers
whenever there are any state changes
- Observers
- Handler for any state change coming from
Subject
- Handler for any state change coming from
Let’s understand this with an example
Example
var ObserverSubject = function () {
this.observers = [];
return {
subscribeObserver: function (observer) {
this.observers.push(observer);
},
unsubscribeObserver: function (observer) {
var index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
},
notifyObserver: function (observer) {
var index = this.observers.indexOf(observer);
if (index > -1) {
this.observers[index].notify(index);
}
},
notifyAllObservers: function () {
for (var i = 0; i < this.observers.length; i++) {
this.observers[i].notify(i);
}
}
};
};
var Observer = function () {
return {
notify: function (index) {
console.log("Observer " + index + " is notified!");
}
};
};
var observerSubject = new ObserverSubject();
var observer1 = new Observer();
var observer2 = new Observer();
var observer3 = new Observer();
var observer4 = new Observer();
observerSubject.subscribeObserver(observer1);
observerSubject.subscribeObserver(observer2);
observerSubject.subscribeObserver(observer3);
observerSubject.subscribeObserver(observer4);
observerSubject.notifyObserver(observer2); // Observer 2 is notified!
observerSubject.notifyAllObservers();
// Observer 1 is notified!
// Observer 2 is notified!
// Observer 3 is notified!
// Observer 4 is notified!
The general idea of this example is to promote loose coupling. Rather than calling methods of ObserverSubject
we subscribe to specific tasks or activities and get notified whenever there is a change.
Real-life Applications
Redux
and React Context
are excellent examples of the Observer patterns. In Redux, we bind component props with the state. Whenever there is any change in prop at state, we notify components using that prop. Similarly for Context, whenever the value is updated, Provider
passes down the change to all the Consumer
either through useContent
hook or Context.Consumer
to render new context values.
Let’s look at the pros and cons of using the Observer design pattern:
Pros
- Loosely coupled blocks of code to improve code management and reuse
- Can be used when there is a one-to-many relationship between objects
Cons
- It is important to understand, that although the observer pattern does offer advantages, one major drawback is a significant drop in performance when the number of observers increases.
- Subscribers are notified in random order
- We have to explicitly register and unregister, or else we’ll be facing memory leak issues
Next, we’ll be understanding the Singleton pattern. Stay tuned for the next post in this series.