The Observer Pattern in JavaScript
The Observer Pattern is one of the most used patterns in modern software. It’s easy to understand, it’s fairly obvious to recognise where it may be useful and it’s straightforward to implement in JavaScript.
The Observer Pattern is one of the most used patterns in modern software. It’s easy to understand, it’s fairly obvious to recognise where it may be useful and it’s straightforward to implement in JavaScript.
Our example: Meeting Room Booking System
It’s nice to learn by using a practical example. Let’s say we have a meeting room booking system. Users can go onto the system and book their own meeting rooms with options (such as tea/coffee/sandwiches/pizza). Once booked, the room is later invoiced for payment by the accounts department.
The point is here that although we have a booker interacting with the booking system, there are other parties interested too (the observers), such as:
- The Office Manager, who needs to know when rooms are occupied so they can be cleaned and prepared.
- The Caterer, who need to supply tea/coffee and food at the appropriate time.
- The Accountant, who will create an invoice for the resource/occupancy time to obtain payment.
Each of these could keep checking the booking database to see if any new bookings have been made, but that would be real pain and very inefficient. What we need is to use the Observer Pattern to register our interest. So each of these can individually subscribe themselves to the booking system to be notified when a new booking is made.
Implementing this is straightforward. We set up an array and the anyone interested in being notified when an event (such as a new booking) takes place can call subscribe which will add them to the observers array. Subscribers can also unsubscribe which simply removes their name from the array.
Every time the target event happens (i.e. a new or altered booking) the booking system calls notifyAll and the Observer Pattern will call the callback function of each subscriber. This is fixed as bookingNotification() in this example but we could have passed the required callback function. We do have the option of only notifying one or a group of selected subscribers only.
The Code:
// *** BOOKING SYSTEM *** var BookingSystem = function () { // this array holds the list of observers this.observers = []; } BookingSystem.prototype = { subscribe: function (observer) { // Observers subscribe by adding their object to our observers array this.observers.push(observer); }, unsubscribe: function (observer) { // Observers unsubscribe by removing their object from our observers array // Note that array.indexOf needs IE9 or above. // If lower then IE9, write a function to loop and find the index. var index = this.observers.indexOf(observer); if (index >= 0) { this.observers.splice(index, 1); } }, notify: function (observer) { // notify a single observer that an event has happened var index = this.observers.indexOf(observer); if (index >= 0) { this.observers[index].bookingNotification(index); } }, notifyAll: function () { // notify all observers that an event has happened for (var i = 0; i < this.observers.length; i++) { this.notify(this.observers[i]); }; } } // *** BOOKERS (and interested parties) *** var Booker = function () { } Booker.prototype = { bookingNotification: function (ref) { console.log("Booking Notification ref: " + ref); } } // *** TEST *** var bookings = new BookingSystem(); var manager = new Booker(); var caterer = new Booker(); var accountant = new Booker(); console.log("** Observer Pattern"); console.log("** Subscribe observers"); bookings.subscribe(manager); bookings.subscribe(caterer); bookings.subscribe(accountant); console.log("** Notify manager only"); bookings.notify(manager); console.log("** Notify all bookers"); bookings.notifyAll(); console.log("** unsubscribe caterer"); bookings.unsubscribe(caterer); bookings.notifyAll();
Run and you should get the following in the browser console:
** Observer Pattern ** Subscribe observers ** Notify manager only Booking Notification ref: 0 ** Notify all bookers Booking Notification ref: 0 Booking Notification ref: 1 Booking Notification ref: 2 ** unsubscribe caterer Booking Notification ref: 0 Booking Notification ref: 1
All OK! In practice we may extend the notifications to show new, modified and deleted bookings. This is similar to a news feed having publish/subscriber and using the pub/sub pattern.