Applying publisher subscriber design pattern in javascript
Tags: javascript, patterns, programming, tutorial
Published: 2022-01-30 16:01:45

## Problem

MeowLand is ruled by King Whiskers. Recently, MeowLand has experiencing rapid growth. One night, King Whiskers decide that he needs to upgrade the knowledge and skills of the catizens of MeowLand in order to improve the CDP and sustain the growth of the population. He aims to do that by regularly disseminating information from the palace library.

However, he knows that his catizens are busy cats and he does not want to overwhelm them with irrelavant news.

The catizens are largely distributed across this industry:

- Mouse hunter
- Bird hunter
- Comedian
- Glam Vloggers
- Daredevils

## Solution

Looking at the problem, we can form a few premises.

- Information that needs to be pushed
- Catizens will receive the information
- Information will be disperse based on the topic of interest relevant to the catizen
- Catizens can have more than one topic of interest
- Catizens can change interest and stop update from a particular topic

For this problem, we will look at the <strong>publisher/subscriber</strong> pattern

## Publisher/Subscriber Pattern

The **Publisher/Subscriber** pattern or the PubSub pattern for short, is a behavioral design pattern. As it's name suggest, it is a design pattern for a subject (Publisher) to notify changes to a list of observers (Subscriber).

Perhaps, you have use the <a href="https://rxjs.dev/guide/overview" target="_blank">RxJS library</a>, and the term Observer seems familiar. You are right, the PubSub design pattern is a variation of of the <a href="https://sourcemaking.com/design_patterns/observer" target="_blank">Observer pattern </a> and RxJS utilises this pattern. Another common implementation of the observer pattern will be the `connect` method in Redux.

<blockquote>

<em>Observer pattern define by GoF</em>

<div>
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
</div>

<div class="text-right">-- <em>Design Patterns: Elements of Reusable Object-Oriented Software </em></div>
</blockquote>

The benefit of implementing an Observer pattern is that two objects are loosely coupled and minimize interdependency between objects. However, because of decoupling, it can sometimes become difficult to ensure that the various parts of the application is working as part of the application can crash without affecting other part of the system.

There is a small difference between the two patterns. For the PubSub pattern, there is a topic/event channel that sits between the publisher and the subscriber. This event system allows the subscriber to be notify of specific events.

<img src="https://res.cloudinary.com/thegeekwing/image/upload/v1584189742/blog/2020/03/pubsub/Observer_pattern.jpg">

## Code

Now that we have the overview of this design pattern, we will create the code for King Whisksers.

Firstly, we will create the `Publisher` class, which will register the subscribers and publish events to the subscribers. For the method `subscribe`, the argument `event` will be the key that the subscribers is listening to. In our case, a bird hunter will need to listen to `climb trees` events.

When King Whiskers will like to publish a `climb trees` news, we will find the `climb trees` key in subscribers and invoke the callback register during subscription.

To simplify unsubscription, we will pass the `unsubscribe` method to be register with the subscribers. In a perfect world, we will create an advanced PubSub which will handle the unsubscripton through the event and the topic but that will introduce a more abstracted code.

<blockquote>
To understand how to implement a PubSub with nested arguments, check out Jack Lawson's Mediator.js. 
<div>
<strong>Read</strong>: <a href="http://thejacklawson.com/Mediator.js/" target="_blank">Mediator.js</a>
</div>
</blockquote>

<div class="code">
<label>PubSub.js</label>

```js
class PubSub {
  constructor() {
    this.subscribers = {};
  }

  subscribe(event, callback) {
    if (!this.subscribers[event]) {
      this.subscribers[event] = [];
    }
    const index = this.subscribers[event].push(callback) - 1;
    const { subscribers } = this;

    return {
      unsubscribe: function() {
        subscribers[event].splice(index, 1);
      },
    };
  }

  publish(event, data) {
    if (!this.subscribers[event]) {
      return;
    }
    this.subscribers[event].forEach(subscriberCallback =>
      subscriberCallback(data)
    );
  }
}
```

</div>

Next, we will create our `Cat` class. Do note that, as mention above, in the perfect world, our `Cat` class will not need to handle subscription.

<div class="code">
<label>Cat.js</label>

```js
class Cat {
  constructor(name, interests) {
    this.name = name;
    this.interests = interests;
    this.unsubscribe = {};
  }

  addUnsubscription(keyName, method) {
    this.unsubscribe[keyName] = method;
  }
}
```

</div>

Then, we will set up the PubSub and test if everything is working based on the 5 premises mention above.

<div class="code">
<label>catDomPubSub.js</label>

```js
const catDomPubSub = new PubSub();

const cat1 = new Cat('Midnight', ['climb trees', 'hunt', 'weather']);
const cat2 = new Cat('Bear', ['humour', 'weather', 'camera skills']);
const cat3 = new Cat('Smokey', ['hunt', 'camera skills']);
const allCat = [cat1, cat2, cat3];

allCat.forEach((singleCat, idx) => {
  const { name, interests } = singleCat;
  interests.forEach(interest => {
    const { unsubscribe } = catDomPubSub.subscribe(interest, data =>
      printInterestReceived(name, interest, data)
    );
    allCat[idx].addUnsubscription(interest, unsubscribe);
  });
});

function printInterestReceived(name, interest, data) {
  console.log(`${name} has received information for ${interest}: ${data}`);
}

catDomPubSub.publish('climb trees', 'Learn coordination');
catDomPubSub.publish('weather', 'Might rain tomorrow, stay indoors!');
catDomPubSub.publish(
  'hunt',
  'Predicted migration of house rats tomorrow, stay alert'
);

cat1.unsubscribe.hunt();

catDomPubSub.publish('hunt', 'Sharpen your claws');
```

If we run this code, we will see the following.

```console
Midnight has received information for climb trees: Learn coordination

Midnight has received information for weather: Might rain tomorrow, stay indoors!
Bear has received information for weather: Might rain tomorrow, stay indoors!

Midnight has received information for hunt: Predicted migration of house rats tomorrow, stay alert
Smokey has received information for hunt: Predicted migration of house rats tomorrow, stay alert

Smokey has received information for hunt: Predicted migration of house rats tomorrow, stay alert
```

Because Midnight unsubscribes from `hunt`, the last publish of hunt will not show Midnight.

Finally, we can show our product to King Whiskers.

This is the gist of a simple example of the PubSub model.

Is this consider completed? No, because we did not store the notifications in each individual `Cat`. For example, the cat could be a level of each skillsets that you are keeping track of based on the publications they receive. Upon each update, they will improve their experience require to level up. Till next time, we will explore more alternatives with the <strong>Observer</strong> and <strong>PubSub</strong> design pattern.