The Observer Design Pattern is a behavioral design pattern that allows multiple objects to be notified and updated when the state of another object changes. Software applications that require event handling or notification of changes widely use this pattern. The observer pattern is a powerful way to create loosely coupled systems that are highly modular and extensible. In this article, we will discuss the Observer Design Pattern, its benefits, and how to implement it.
Benefits of Observer Design Pattern
The Observer Design Pattern has several benefits, including
Loose coupling: The observer pattern allows for loose coupling between the observer and the subject. The observer does not need to know the implementation details of the subject, and the subject does not need to know about the observers. This loose coupling makes the system more modular and easier to maintain.
Extensibility: The observer pattern allows new observers to be added easily without changing the subject’s code. This makes the system more extensible, and new features can be added without affecting the existing code.
Simplified event handling: The observer pattern simplifies event handling by allowing the subject to notify all observers when an event occurs. This eliminates the need for the observers to poll the subject for changes.
Reusability: The observer pattern promotes code reuse by allowing the same observer to be used by multiple subjects..
Implementation of Observer Design Pattern
The Observer Design Pattern consists of two main components: the subject and the observer. The subject is the object that is being observed, and the observer is the object that is notified when the subject’s state changes.
The following are the steps for implementing the Observer Design Pattern:
Step 1: Define the Subject interface
The Subject interface defines the methods that the observer will use to register, unregister, and notify the observers. The interface should have methods such as addObserver(), removeObserver(), and notifyObservers().
Step 2: Implement the Subject class
The Subject class should implement the Subject interface and maintain a list of observers. The class should also have methods to add and remove observers from the list and notify all the observers when the state of the subject changes.
Step 3: Define the Observer interface
The Observer interface defines the method that the subject will call when its state changes. The interface should have a method such as update().
Step 4: Implement the Observer class
The Observer class should implement the Observer interface and define the update() method. The subject call this method when its stage changes.
Step 5: Register the Observer with the Subject
To receive notifications from the subject, the observer needs to register itself with the subject. This is done by calling the addObserver() method on the subject and passing in the observer object.
Step 6: Notify Observers
When the state of the subject changes, the subject should call the notifyObservers() method to notify all the registered observers. The method should iterate through the list of observers and call the update() method on each observer.
Example
We can export a singleton Observer object, which contains a notify, subscribe, and unsubscribe method.
const observers = [];
export default Object.freeze({
notify: (data) => observers.forEach((observer) => observer(data)),
subscribe: (func) => observers.push(func),
unsubscribe: (func) => {
[...observers].forEach((observer, index) => {
if (observer === func) {
observers.splice(index, 1);
}
});
},
});
We can use this observable throughout the entire application, making it possible to subscribe functions to the observable
import Observable from "./observable";
function logger(data) {
console.log(`${Date.now()} ${data}`);
}
Observable.subscribe(logger);
import './analytics.js';
import Observer from './observer.js';
Observer.notify('✨ New data ✨');
setTimeout(() => {
Observer.notify('✨ New data after timeout ✨');
}, 1000);
Result
~/projects/node-4u1xzk
❯ node index.js
Sent to Google analytics: ✨ New data ✨
Sent to custom analytics: ✨ New data ✨
Sent to email: ✨ New data ✨
Sent to Google analytics: ✨ New data after timeout ✨
Sent to custom analytics: ✨ New data after timeout ✨
Sent to email: ✨ New data after timeout ✨
Comparison with and without Observer design pattern.
Without observer design pattern:
import './style.css';
import {
sendToGoogleAnalytics,
sendToCustomAnalytics,
sendToEmail,
} from './analytics.js';
const pinkBtn = document.getElementById('pink-btn');
const blueBtn = document.getElementById('blue-btn');
pinkBtn.addEventListener('click', () => {
const data = '🎀 Click on pink button! 🎀';
sendToGoogleAnalytics(data);
sendToCustomAnalytics(data);
sendToEmail(data);
});
blueBtn.addEventListener('click', () => {
const data = '🦋 Click on blue button! 🦋';
sendToGoogleAnalytics(data);
sendToCustomAnalytics(data);
sendToEmail(data);
});
With observer design pattern:
import './style.css';
import Observer from './observer.js';
import './analytics.js';
const pinkBtn = document.getElementById('pink-btn');
const blueBtn = document.getElementById('blue-btn');
pinkBtn.addEventListener('click', () => {
const data = '🎀 Click on pink button! 🎀';
Observer.notify(data);
});
blueBtn.addEventListener('click', () => {
const data = '🦋 Click on blue button! 🦋';
Observer.notify(data);
});
Conclusion
The Observer Design Pattern is a powerful way to create loosely coupled systems that are highly modular and extensible. By using this pattern, we can create objects that are decoupled and independent, making them easier to test, maintain, and reuse. The Observer Design Pattern is widely used in event-driven systems such as GUI frameworks, where changes to the GUI elements need to be propagated to all interested parties.
Would you like to read more articles by Tekos’s Team? Everything’s here.