Design Pattern in JavaScript - Part 3

Observer pattern

In the previous post, we discussed the benefits of prototype patterns, why they can be helpful and when can it create problems.

Observer design pattern

The observer is a design pattern in which when one part of the application changes and other parts are needed to be updated. If the subscriber object is changed/modified, it broadcasts its changes to dependent objects. This is an example of event-driven programming in JavaScript. It facilitates object-oriented design. Another for the Observer pattern is pub/sub, short for Publication/Subscription.

However, Publish/Subscribe patterns use a topic/event channel that sits in between participants to receive notifications and the object firing the event.

Diagram

Screenshot 2022-07-02 at 1.20.08 PM.png

Participants

The objects participating in these patterns are:

  • Observer Subject
    • Maintains a list of observers
    • Interface for observers to subscribe or unsubscribe
    • Notify observers whenever there are any state changes
  • Observers
    • Handler for any state change coming from Subject

Let’s understand this with an example

Example

var ObserverSubject = function () {
  this.observers = [];

  return {
    subscribeObserver: function (observer) {
      this.observers.push(observer);
    },
    unsubscribeObserver: function (observer) {
      var index = this.observers.indexOf(observer);
      if (index > -1) {
        this.observers.splice(index, 1);
      }
    },
    notifyObserver: function (observer) {
      var index = this.observers.indexOf(observer);
      if (index > -1) {
        this.observers[index].notify(index);
      }
    },
    notifyAllObservers: function () {
      for (var i = 0; i < this.observers.length; i++) {
        this.observers[i].notify(i);
      }
    }
  };
};

var Observer = function () {
  return {
    notify: function (index) {
      console.log("Observer " + index + " is notified!");
    }
  };
};

var observerSubject = new ObserverSubject();

var observer1 = new Observer();
var observer2 = new Observer();
var observer3 = new Observer();
var observer4 = new Observer();

observerSubject.subscribeObserver(observer1);
observerSubject.subscribeObserver(observer2);
observerSubject.subscribeObserver(observer3);
observerSubject.subscribeObserver(observer4);

observerSubject.notifyObserver(observer2); // Observer 2 is notified!

observerSubject.notifyAllObservers();
// Observer 1 is notified!
// Observer 2 is notified!
// Observer 3 is notified!
// Observer 4 is notified!

The general idea of this example is to promote loose coupling. Rather than calling methods of ObserverSubject we subscribe to specific tasks or activities and get notified whenever there is a change.

Real-life Applications

Redux and React Context are excellent examples of the Observer patterns. In Redux, we bind component props with the state. Whenever there is any change in prop at state, we notify components using that prop. Similarly for Context, whenever the value is updated, Provider passes down the change to all the Consumer either through useContent hook or Context.Consumer to render new context values.

Let’s look at the pros and cons of using the Observer design pattern:

Pros

  • Loosely coupled blocks of code to improve code management and reuse
  • Can be used when there is a one-to-many relationship between objects

Cons

  • It is important to understand, that although the observer pattern does offer advantages, one major drawback is a significant drop in performance when the number of observers increases.
  • Subscribers are notified in random order
  • We have to explicitly register and unregister, or else we’ll be facing memory leak issues

Next, we’ll be understanding the Singleton pattern. Stay tuned for the next post in this series.