The Mediator Pattern in JavaScript

jsmanifest - Oct 30 '19 - - Dev Community

Find me on medium
Join my newsletter

In JavaScript, a widely used and powerful pattern is the Module Pattern. It can be incredibly simple to implement but the fact that it enables developers to encapsulate their code makes it one of the most versatile patterns to build robust code. When you look inside the source code of JavaScript libraries, you're most likely looking at an implementation of this pattern. In addition, they're also most likely a singleton object--where only one instance exists throughout the lifetime of an app.

It may be difficult for newcomers in JavaScript to understand the module pattern as there are several variations that exist. However, it's worth all the time and trouble because you'll be using the module pattern to write most of your apps once you've understood the concept.

Variations

Immediately Invoked Function Expression

Arguably the most popular variation of the module pattern is the IIFE (Immediately Invoked Function Expression). These are essentially functions that invoke immediately and should return an object (an interface, in other words), which will be used as the module.

Inside these functions are code that is private and accessible only within that function's scope unless the returned interface (publicly accessible by the outside world) provides methods that can access them somehow.

Modules

As you may have guessed, the module pattern lets you create modules.

We will implement our own module using the IIFE. This allows us to assign the return value of an IIFE directly onto a variable so that we can use it just like a JavaScript module.

For example, lets pretend that we are creating an RPG game and the first thing we decided to do was create a sorceress class. The sorceress will have methods to inflict damage to their targets. In just about every RPG game, sorceresses usually cast spells or magic, so we'll define an interface that mimic this concept:

const sorceress = (function() {
  const sideEffects = {
    intervals: {},
  }

  function _fireBolt(target, customDamage) {
    target.hp -= customDamage !== undefined ? customDamage : 15
  }

  function _thunderBolt(target) {
    target.hp -= 15
  }

  function blizzard(target) {
    target.hp -= 15
  }

  function _applyThunderBoltSideEffects(
    target,
    { interval = 1000, timeout = 15000 } = {},
  ) {
    if (sideEffects.intervals[target.id]) {
      clearInterval(sideEffects.intervals[target.id])
    }

    sideEffects.intervals[target.id] = setInterval(() => {
      target.hp -= 1
    }, interval)

    setTimeout(() => {
      if (sideEffects.intervals[target.id]) {
        clearInterval(sideEffects.intervals[target.id])
      }
    }, timeout)
  }

  return {
    fireBolt(target, options) {
      if (options) {
        _fireBolt(target, options.customDamage)
      } else {
        _fireBolt(target)
      }
    },
    thunderBolt(target) {
      _thunderBolt(target)
      _applyThunderBoltSideEffects(target)
    },
    blizzard,
  }
})()
Enter fullscreen mode Exit fullscreen mode

In this example, our sorceress class has three methods: sorceress.fireBolt, sorceress.thunderBolt, and sorceress.blizzard.

Inside the module, we declared three private functions and one public function. We can obviously tell that that the functions prefixed with underscores _ are the private functions while the others are public. This isn't how we create public and private code inside though, we do it by what's being returned using closure. We returned an object which is exposed to the caller. Having the power to declare private and public variables is what makes the module pattern one of the most powerful patterns in JavaScript.

By keeping things private we can prevent code from being revealed to the "outside world".

Find me on medium
Join my newsletter

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player