Observer pattern and Pub/Sub pattern in JS
Here I am going to illustrate observer pattern first and its code walk through and then comes to Pub/Sub pattern
Below is a simple implementation of the observer pattern:
function Subject () {
this.observers = [];
}
Subject.prototype = {
getIndex: function(observer) {
return this.observers.indexOf(observer)
},
subscribe: function(observer) {
this.observers.push(observer);
},
unsubscribe: function(observer) {
this.observers = this.observers.filter(item => item !== observer)
},
notify: function (observer) {
let observerIndex = this.getIndex(observer);
if(observerIndex > -1) {
this.observers[observerIndex].notify(observerIndex);
}
},
notifyAll: function () {
this.observers.forEach((observer) => {
observer.notify();
})
},
count: function () {
return this.observers.length;
}
}
function Observer (id) {
return {
id,
notify: function() {
console.log(`Observer ${id} has been notified!`)
}
}
}
const subject = new Subject();
const observer1 = new Observer(1);
const observer2 = new Observer(2);
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.notifyAll() // prints
// Observer 1 has been notified!
// Observer 2 has been notified!
subject.unsubscribe(observer1);
subject.count() // prints 1
subject.notify(observer2) // prints
// Observer 2 has been notified!
In the code above, the Subject
constructor maintains a list of observers. And we added the relevant methods to the prototype of the Subject
. This enables us to share these methods with every instance of the Subject
.
If you do not understand how we can share methods using an object’s prototype, I suggest you read a previous article that covers prototypal inheritance in JavaScript.
In the code above, the subscribe
method adds observers to the observers
array while the unsubscribe
method removes observers from the observers
array. The notify
method would notify the specified subscribed observer while the notifyAll
method notifies all the subscribed observers.
PUB/SUB PATTERN:
The pub/sub broker also enables loose decoupling of publishers and subscribers and it supports many to many relationships between the publishers and the subscribers.
So unlike the observer pattern, the pub/sub pattern allows multiple publishers and multiple subscribers.
In the pub/sub pattern a publisher publishes contents to a topic and interested subscribers access these contents by sending subscriptions to the pub/sub broker to subscribe to that topic. Also, unlike the observer pattern, both the publishers and the subscribers do not need to be aware of each other.
class Pubsub {
constructor() {
this.events = {};
}
subscription (eventName, func) {
return {
subscribe: () => {
if (this.events[eventName]) {
this.events[eventName].push(func);
console.log(`${func.name} has subscribed to ${eventName} Topic!`)
} else {
this.events[eventName] = [func];
console.log(`${func.name} has subscribed to ${eventName} Topic!`)
}
},
unsubscribe: () => {
if(this.events[eventName]){
this.events[eventName] = this.events[eventName].filter((subscriber) => subscriber !== func);
console.log(`${func.name} has unsubscribed from ${eventName} Topic!`)
}
}
}
}
publish(eventName, ...args) {
const funcs = this.events[eventName];
if (Array.isArray(funcs)) {
funcs.forEach((func) => {
func.apply(null, args);
});
}
}
}
const speak = (param) => {
console.log(`I am ${param}`);
};
const greetAll = (x, y, z) => {
console.log(`Hello ${x}, ${y}, ${z}`);
};
const pubsub = new Pubsub();
pubsub.subscription("greet", greetAll).subscribe() // prints greetAll has subscribed to greet Topic!
pubsub.subscription("sayName", speak).subscribe() // prints speak has subscribed to sayName Topic!
pubsub.subscription("sayName", greetAll).unsubscribe() // prints greetAll has unsubscribed from sayName Topic!
pubsub.publish("greet", "Lawrence Eagles", "John Doe", "Jane Doe"); // prints Hello Lawrence Eagles, John Doe, Jane Doe
pubsub.publish("sayName", "Lawrence Eagles"); // prints I am Lawrence Eagles
In our small example above, the subscription
method returns an object containing a subscribe
method used to handle subscriptions and an unsubscribe
method that handles unsubscriptions.
Some examples of real-life applications that use the pub/sub pattern are Redis, Split, Twillo, and Gutenberg created by Netflix.