Observer design pattern is a behavioral design pattern. There are two types of objects involved in observer design pattern, subject and observer. The subject object maintains a list of observer objects which depend on the subject and notifies observers whenever subject's state changes.
The definition of the Observer pattern provided in the GoF book, Design Patterns: Elements of Reusable Object-Oriented Software, is:
"One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves."
Let's take a real life example. Suppose there is a video sharing platform, there are many viewers as well as many channels, if a viewer is interested in a certain channel's videos, he has to check the channel again and again to see if there is a new video published which will be a waste of time if the video is not published yet. On the other hand if we decide to notify every viewer about every channel's video when it gets published, there will be a clutter of notifications of new videos from each channel. This would upset the customers who are not interested in certain channels. So now we have got a conflict, by notifying each customer about every video we will be wasting resources and if customer has to check every channel, it will be a waste of time for them.
If we use observer pattern in this scenario, if a viewer(observer) is interested in a channel(subject), he will subscribe to that channel and he will get notified whenever that channel publishes a new video. On the other hand other viewers who did not subscribe to that channel won't get any notification. Each channel will maintain a list of subscribers to notify about new videos. Each subscriber can also unsubscribe to a channel if they are not interested in that channel anymore.
Structure
Note: I have used video as string because it will get too complex if I add the logic to add a video.
The notify
method will log on the console because I want to avoid implementing complex logic of sending notifications and focus on implementing design pattern
Implementation
Since subject uses list to store observers, let's first make a list of subscribers.
// Constructor for defining new SubscriberList
function SubscriberList(){
this.subscribers = []; // an empty array by default
}
// defining methods for subscriber list.
// Subscribe method to add new subscribers
SubscriberList.prototype.add = function (subscriber){
this.subscribers.push(subscriber);
}
// unsubscribe method to remove unsubscribers
SubscriberList.prototype.remove = function (unsubscriber){
this.subscribers = this.subscribers.filter(subscriber => (subscriber != unsubscriber) );
}
The business logic of our example is dependent upon uploading videos and then notifying the user. So we need to define a video list too that every channel will have.
// constructor defining new VideoList.
function VideoList(){
this.videos = []; // an empty array by default.
}
// defining methods for video list.
// add method to add new Videos.
VideoList.prototype.add = function (video){
this.videos.push(video);
}
// remove method to remove videos.
VideoList.prototype.remove = function (delVideo){
this.videos = this.videos.filter(video => (video != delVideo) );
}
Let's work on Channel
(subject) constructor method now,
// make a constructor method for Channel
function Channel(name = "unknown") {
this.channelName = name;
this.subscriberList = new SubscriberList();
this.videoList = new VideoList();
}
// defining a subscribe method to add subscriber to the subscriberList
Channel.prototype.subscribe = function(subscriber) {
this.subscriberList.add(subscriber);
}
// defining an unsubscribe method to remove
// unsubscribers from subscribersList
Channel.prototype.unsubscribe = function(unsubscriber) {
this.subscriberList.remove(unsubscriber);
}
// defining a notifySubscribers method to
// send an update to each subscriber
Channel.prototype.notifySubscribers = function(videoName) {
this.subscriberList.subscribers.forEach(subscriber => {
subscriber.update(videoName, this.channelName);
})
}
// defining an uploadVideo method to add videos to videoList
// and then notify all subscriber about upload
Channel.prototype.uploadVideo = function(video) {
this.videoList.add(video);
this.notifySubscribers(video)
}
// defining an deleteVideo method to remove a video from VideoList
Channel.prototype.deleteVideo = function(delVideo) {
this.subscriberList.remove(delVideo);
}
Now let's make constructor for our Viewer
(observer)
function Viewer() {
this.update = function(videoName, channelName) {
console.log(channelName + " has uploaded a new video " + videoName)
}
}
Let's make objects and run the code.
function run() {
let learnDesignPatterns = new Channel("Learn design patterns");
let mike = new Viewer();
let phil = new Viewer();
learnDesignPatterns.subscribe(mike);
learnDesignPatterns.subscribe(phil);
learnDesignPatterns.uploadVideo("singleton");
}
run();
When To Use Observer Design Pattern
- When some objects need to observe state of an object but for some specific cases or for limited time.
- when changing in an object may require change in another object.
Advantages
- You can add many observers without changing the code of subject and vice versa. So it supporst extensibility.
- Relation between objects can be established and removed at runtime.
Disadvantages
- If not implemented correctly it can be complex and lead to performance issues.
- There is also a memory leakage problem in the observer design pattern because of the observer's explicit subscribe and unsubscribe.