The Facade Design Pattern in JavaScript

Christopher T.

June 11th, 2022

Share This Post

When developing applications in JavaScript it's not uncommon to be encountering redundant code that make us question its positive impact in value. Redundancy is a common practice that developers often try to avoid doing because too much redundancy easily leads to unpleasant development experiences like increases in code size as well as decreases in code maintainability.

One great way to solve this issue is the Facade Design Pattern. In this post we will be going over the Facade and how it is an effective pattern to solve issues that are similar to this.

In addition to reducing redundancy the facade pattern also does a good job at defining a higher level interface that unify several interfaces to into more elegant interfaces with the intention of simplifying things more. It's commonly used to wrap unnecessarily complex parts of code into a simpler structure so that the client isn't responsible for steps they don't care about.

facade-design-pattern-unifying-complex-interface-to-simple-interface

Let's pretend we are building a simple game and one of our first implementation of something will be a definition of a Human class. This human class will be expected by other parts of our code to contain several body parts as properties: Eye, Ear, Arm, Leg, Feet, Nose, Mouth, Neck, and Stomach:

class Human {
  constructor(opts = {}) {
    this.leftEye = opts.leftEye
    this.rightEye = opts.rightEye
    this.leftArm = opts.leftArm
    this.rightArm = opts.rightArm
    this.leftFoot = opts.leftFoot
    this.rightFoot = opts.rightFoot
    this.leftEar = opts.leftEar
    this.rightEar = opts.rightEar
    this.nose = opts.nose
    this.mouth = opts.mouth
    this.neck = opts.neck
    this.stomach = opts.stomach
  }
}

class Eye {}
class Ear {}
class Arm {}
class Leg {}
class Feet {}
class Nose {}
class Mouth {}
class Neck {}
class Stomach {}

Let's now define a Profile class which has a method for setting its profile character called setCharacter:

class Profile {
  setCharacter(character) {
    validateCharacter(character)
    this.character = character
  }
}

In a real world scenario of course it would be dozens of times longer than this so just keep in mind that we are focusing solely on the pattern and the problem it solves by presenting only the relative code.

We included a validateCharacter in the beginning of our setCharacter function because it's a necessary component of any software to validate constructed objects to ensure that errors don't occur unknowingly. This also happens to be a great setup to demonstrate the Facade.

Here is the implementation:

function validateCharacter(character) {
  if (!character.leftEye) throw new Error(`Missing left eye`)
  if (!character.rightEye) throw new Error(`Missing right eye`)
  if (!character.leftEar) throw new Error(`Missing left ear`)
  if (!character.rightEar) throw new Error(`Missing right ear`)
  if (!character.nose) throw new Error(`Missing nose`)
  if (!character.mouth) throw new Error(`Missing mouth`)
  if (!character.neck) throw new Error(`Missing neck`)
  if (!character.stomach) throw new Error(`Missing stomach`)
}

So inside our validateCharacter call it proceeds to check every part of the human body and throws an error if at least one body part is missing.

Let's try to use our code as if we were the client:

const bob = new Human()
const bobsProfile = new Profile()
bobsProfile.setCharacter(bob)

Running the code will result with an error:

Error: Missing left eye

So how does the client fix this? Easy! They just need to construct every single body part and be responsible for making them exist on the instance:

const bobsLeftEye = new Eye()
const bobsRightEye = new Eye()
const bobsLeftEar = new Ear()
const bobsRightEar = new Ear()
const bobsNose = new Nose()
const bobsMouth = new Mouth()
const bobsNeck = new Neck()
const bobsStomach = new Stomach()
const bobsLeftArm = new Arm()
const bobsRightArm = new Arm()
const bobsLeftLeg = new Leg()
const bobsRightLeg = new Leg()
const bobsLeftFoot = new Feet()
const bobsRightFoot = new Feet()

bob.leftEye = bobsLeftEye
bob.rightEye = bobsRightEye
bob.leftEar = bobsLeftEar
bob.rightEar = bobsRightEar
bob.nose = bobsNose
bob.mouth = bobsMouth
bob.neck = bobsNeck
bob.stomach = bobsStomach
bob.leftArm = bobsLeftArm
bob.rightArm = bobsRightArm
bob.leftLeg = bobsLeftLeg
bob.rightLeg = bobsRightLeg
bob.leftFoot = bobsLeftFoot
bob.rightFoot = bobsRightFoot

Now our code runs without errors. If you went along hands-on however you might have noticed that we only cared about creating the character and profile. Notice we encountered some issues here:

  1. The code is longer
  2. The code is more complex
  3. The code is not very easy to use for the client (in comparison to the original three liner)
  4. Redundancy - There is redundancy in multiple areas. One hard hit to the redundancy is the repetitive mentioning of "bob" throughout our code.
  5. We gave the ball to the client (in other words we forced users of our code to become responsible for constructing and passing in every single body part). A lot of times this becomes a good thing--it's not uncommon that we want to give the client code the ability to call the shots. But this situation is much different. There is no point here in having them do the work if they choose to go straight to creating the profile and proceeding solely on the profile's features.

Now lets make our Facade and define how it will solve these issues for clients:

class Profile {
  setCharacter(character) {
    if (!character.leftEye) character.leftEye = new Eye()
    if (!character.rightEye) character.rightEyye = new Eye()
    if (!character.leftEar) character.leftEar = new Ear()
    if (!character.rightEar) character.rightEar = new Ear()
    if (!character.nose) character.nose = new Nose()
    if (!character.mouth) character.mouth = new Mouth()
    if (!character.neck) character.neck = new Neck()
    if (!character.stomach) character.stomach = new Stomach()
    this.character = character
  }
}
const bob = new Human()
const bobsProfile = new Profile()
bobsProfile.setCharacter(bob)

Our Profile becomes the Facade itself and effectively encapsulates every implementation of body parts as a fallback so that the client code only needs to focus on the interface provided by the Profile.

We not only give them the option to skip the unnecessary steps to construct and set each body part, we also give them the option to be fully in control of that if they wanted to on their own.

Also, notice that the user of our code is tightly coupled to the interface that Profile exposes.

Here is an example taken from a gist that implements a facade. The intent of the pattern in this example is to encapsulate multiple operations into one single call. In a business logic perspective this could represent an interface for client code to perform an online e-book purchase where the client doesn't really care about how the book is purchased--it only needs a "save" and "send" behavior for their users:

var module = (function () {
  'use strict'

  // Private Method
  var book = {
    get: function () {
      // Find book with the id/index
      console.log('Getting Book Info...')
    },
    set: function (bID, uID) {
      // use bookID(bID) and userID(uID) to set book to user
    },
    download: function () {
      console.log('downloading')
    },
    mailing: function () {
      console.log('Thank You for Subscribing.')
    },
  }

  return {
    // Create the facade for running whole list of process
    facade: function (data) {
      book.set(data.id, data.userID)
      book.get()
      if (data.download) book.download()
      if (data.mailing) book.mailing()
    },
  }
})()

The client code than uses this interface and doesn't need to worry about the implementation details:

module.facade({
  id: 2,
  userID: 123123,
  download: true,
  mailing: true,
})

Real World Analogy

Let's take grocery stores as an example. When you buy groceries and the cashier informs you of the price of the goods you are paying for, you have the option of paying in cash or credit card. Although these two methods of payment are different they both ultimately achieve the same goal. Nowadays since the covid19 pandemic a new popular method of payment is the tap to pay which again is different in the sense that we no longer require to swipe or exchange some form of money.

This is the facade pattern where it provides a new way to pay (a new interface) to users that do the same thing.

Real World Code Example

ts-morph

One of my top most favorite open sourced projects is ts-morph. This library serves as a wrapper around the TypeScript compiler API to provide an "easier way to programmatically navigate and manipulate TypeScript and JavaScript code". The original TypeScript compiler API can easily become complex, so ts-morph encapsulates these complexities and exposes an entirely new easy-to-use interface to the client code to use instead. I am a frequent user of ts-morph so I can vouch on a personal level that this has made development with the TypeScript API much more easier.

Differences from the Adapter and Flyweight pattern

Facade vs Adapter

Sometimes the facade design pattern can be mistaken for the Adapter design pattern. But the difference is that the Facade may expose an entirely new interface for the client to use whereas the Adapter's intent is to be backwards compatible with previous interfaces when seeking to extend with newer properties and/or behavior.

Facade vs Flyweight

In the facade, the client code is given an interface to work with that represents an entire system of objects (which can contain new copies of identical objects) while the client in the flyweight pattern is given an interface to produce objects that intend to be shared when identical which is an effective approach to preserve memory.

Conclusion

And that concludes the end of this post! I hope you found this to be valuable and look out for more in the future!


Top online courses in Web Development

Tags


Read every story from jsmanifest (and thousands of other writers on medium)

Your membership fee directly supports jsmanifest and other writers you read. You'll also get full access to every story on Medium.

Subscribe to the Newsletter

Get continuous updates

Mediumdev.toTwitterGitHubrss

© jsmanifest 2022