Observer and Publish-Subscribe (Pub/Sub) are two design patterns that facilitate how different components of a system communicate with each other. They belong to the family of behavioral design patterns.
In this answer, I will explain both design patterns, their use cases, and how to implement them in a Node.js application through examples. Let’s start with the Observer pattern.
**Observer Pattern**
The Observer pattern defines a one-to-many relationship between an object (subject) and multiple other objects (observers). Whenever there is a change in the state of the subject, all the observers are notified, and they can update themselves accordingly.
_When to use_: The Observer pattern is best used when you need to maintain a dynamic relationship between one or many components in a system reacting to the changes of another component.
_Implementation in Node.js_:
For this example, I will create a simple application where a Subject manages customer observers who would like to be notified about discount information.
// Defining the Subject class
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observerToRemove) {
this.observers = this.observers.filter((observer) => observer !== observerToRemove);
}
notify(data) {
this.observers.forEach((observer) => observer.update(data));
}
}
// Defining the Observer class (customers)
class Customer {
constructor(name) {
this.name = name;
}
update(discountInfo) {
console.log(`${this.name}, there is a discount for: ${discountInfo}`);
}
}
// Create a Subject instance and Observer instances
const store = new Subject();
const alice = new Customer('Alice');
const bob = new Customer('Bob');
// Add observers
store.addObserver(alice);
store.addObserver(bob);
// Notify observers
store.notify('50% sale on shoes');
**Publish-Subscribe (Pub/Sub) Pattern**
The Publish-Subscribe (or Pub/Sub) pattern is another way to implement communication between components of a system. It utilizes message queues (broker or event channel) to decouple both the sender (publisher) and the receiver (subscriber) components. Publishers send messages to a topic or channel, and subscribers listen for messages on that topic or channel. Whenever there is a message, subscribers receive the message and process it.
_When to use_: The Pub/Sub pattern is particularly suitable for distributed, event-driven systems where components should not be directly aware of each other. For example, when you have multiple services that need to collaborate but remain loosely-coupled.
_Implementation in Node.js_:
For this example, I will create a simple EventEmitter-based implementation of the Pub/Sub pattern, where a publisher notifies its subscribers about new articles.
const EventEmitter = require('events');
class Publisher extends EventEmitter {}
// Defining the Subscriber class (readers)
class Subscriber {
constructor(name) {
this.name = name;
}
read(title) {
console.log(`${this.name} is reading article: ${title}`);
}
}
// Create a Publisher instance and Subscriber instances
const publisher = new Publisher();
const alice = new Subscriber('Alice');
const bob = new Subscriber('Bob');
// Subscribe to the 'new-article' event
publisher.on('new-article', (title) => {
alice.read(title);
});
publisher.on('new-article', (title) => {
bob.read(title);
});
// Publish a 'new-article' event
publisher.emit('new-article', 'How to code in Node.js');
Both the Observer pattern and the Pub/Sub pattern are used to facilitate communication between components, but they differ in certain aspects. The Observer pattern implies tight coupling between the subject and its observers, whereas the Pub/Sub pattern promotes loose coupling through message brokers or event channels. You can choose which pattern to use based on your application’s requirements and the degree of coupling you desire.