The Bridge Design Pattern in JavaScript
Tags: node, javascript, webdev, programming
Published: 2022-04-30 23:49:04


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8c1bql0oyrgiw7kf206.jpg)

In this article we will be going over the Bridge Design Pattern in JavaScript. This is one of the top used patterns that make a significant impact in softare applications. It is a pattern that easily promotes a separation of concerns in its implementation and it's _scalable_.

Here is diagram depicting this pattern:

![javascript-bridge-design-pattern-diagram](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/bridge-design-pattern-in-javascript/javascript-bridge-design-pattern-diagram.png)

There are usually two main _participants_ (or _entity_, whichever you want to call it) that are involved in the Bridge Pattern.

The first and top most part is the abstract layer. This can be implemented simply as a class:

```js
class Person {
  constructor(name) {
    this.name = name
  }

  talk(message) {
    console.log(message)
  }
}
```

In the Bridge Pattern, the abstract layer declares the base interface methods and/or properties. However, they do not care about the implementation details because that isn't their job. To be able to reap the advantages of this pattern it must be kept this way so that our code later does not become tightly coupled and remains manageable.

The abstract layer instead _opens bridges_ which then leads to the second main part of the pattern: the _implementation_ layers (which are often implemented as _classes_ in practice) are attached to these bridges, which the _client_ (or _you_) call the shots. The word "attached" is my form of a human readable term to understand the code term which are _references_ or _pointers_:

![bridge-pattern-real-life-vs-code-diagram1.png](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/bridge-design-pattern-in-javascript/bridge-pattern-real-life-vs-code-diagram1.png 'Bridge Pattern Real Life Vs Code Diagram 1 Png')

The "bridge" can visibly appear in code like this:

```js
class Theme {
  constructor(colorScheme) {
    this.colorScheme = colorScheme // Bridge declared
  }

  getColorScheme() {
    return this.colorScheme // Bridge reference/pointer
  }
}
```

If you've visited websites like `https://dev.to` or `https://medium.com` they have a theme feature that you can access inside your profile. There is usually a _toggle_ theme button. The theme is the abstract layer. The actual implementation in toggling between light and dark are most likely located _outside_ of the abstract layer location within the implementation layer(s).

## Where and when should the Bridge Pattern be used?

Some implementations in the real world are coded in a way where the "bridge effect" goes "live" during _run time_. When you need this type of coupling / binding between two objects this is when you can use the Bridge Pattern to your advantage.

A good example of this is [twilio-video](https://github.com/twilio/twilio-video.js), a JavaScript library that lets you add real time voice and video to your web applications (like Zoom). In this library, The [Room](https://github.com/twilio/twilio-video.js/blob/master/lib/room.js) always instantiates as an empty room. The class keeps a pointer to a `LocalParticipant`, (when you join a video chat room _you_ are the `LocalParticipant` on your screen) but the `LocalParticipant` doesn't actually run or become instantiated _yet_ until it connects and is finished subscribing to the room which is only possible in running code.

If you scan through their code you will spot bridges in a lot of areas. A video chat session cannot be created without a `Room`, and a room does not start until there are at least two `Participant`s. But a `Participant` cannot begin streaming until they start their local audio/video `MediaTrack`s. These classes work together in a top down hierarchy. When you start to have multiple classes that are coupled together this is also a good time to consider the Bridge Pattern.

Another scenario where the Bridge Pattern is useful is when you want to share an implementation of some object with multiple objects.

For example, the [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) class represents a media track for a stream. The two most common implementations that "bridge" from it are [audio and video tracks](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/kind).

![bridge-design-pattern-media-stream-track.png](https://jsmanifest.s3.us-west-1.amazonaws.com/posts/bridge-design-pattern-in-javascript/bridge-design-pattern-media-stream-track.png 'Bridge Design Pattern Media Stream Track Png')

In addition, the implementation details are usually hidden within the derived classes.

## Implementation

Let's implement our own variation of the Bridge Pattern to get a good feel of a problem and solution it brings to the table.

Let's start with a generic `Thing` class which can represent any _thing_:

```js
class Thing {
  constructor(name, thing) {
    this.name = name
    this.thing = thing
  }
}
```

We can create a high level abstraction class that extends `Thing`. We can call this `LivingThing` and will define a method called `eat`. All living things in the real world are born with the ability to eat in order to stay alive. We can mimic this in our code. This will stay in the high level abstract layer:

```js
class LivingThing extends Thing {
  constructor(name, bodyParts) {
    super(name, this)
    this.name = name
    // Bridge
    this.mouth = bodyParts?.mouth || null
  }

  eat(food) {
    this.mouth.open()
    this.mouth.chew(food)
    this.mouth.swallow()
    return this
  }
}
```

We can see that we opened a bridge to the `Mouth` class. Let's define that class next:

```js
class Mouth extends Thing {
  constructor() {
    super('mouth', this)
  }

  chew() {}
  open() {}
  swallow() {}
}
```

The thing (no pun intended) to consider now is that our `Mouth` will be an implementation layer where we write the logic for communicating between the mouth and food.

This implementation is entirely based in `Mouth`. The `LivingThing` does not care about these implementation details and instead delegates this role entirely to its implementation classes which in our case is `Mouth`.

Let's pause and talk about this part for a moment. If `LivingThing` is not involved in any of its implementations this is actually a useful concept to us. If we can make other `LivingThing`s that only need to provide the interface for implementations to derive from, then we can make a wider range of classes for other scenarios.

In an MMORPG game we can use the `LivingThing` and make more of them where they all _inherit_ a _pointer_ to a `mouth` automatically:

```js
class Character extends LivingThing {
  constructor(name, thing) {
    super(name, this)
    this.thing = thing
    this.hp = 100
    this.chewing = null
  }

  attack(target) {
    target.hp -= 5
    return this
  }

  chew(food) {
    this.chewing = food
    return this
  }

  eat(food) {
    this.hp += this.chewing.hpCount
    return this
  }
}

class Swordsman extends Character {}
class Rogue extends Character {}
class Archer extends Character {}
class Sorceress extends Character {}

class Potion {
  constructor(potion) {
    this.potion = potion
  }

  consume(target) {
    if (this.potion) {
      this.eat(this.potion)
      this.potion = null
    }
  }
}

class Food {...}

const sally = new Sorceress()
const mike = new Rogue()

mike.attack(sally)
sally.eat(new Food(...))
```

The bridge pattern is well known to enable developers to build cross-platform applications. We can already see this capability in our examples. We can build this same MMORPG game by reusing `LivingThing` on a new code base. We only need to re-implement the implementation layers like `Mouth` in order to create bindings to different platforms.

We aren't limited to games. Since our `LivingThing` is generic and makes sense for anything that _moves_ it's possible we can use it to create something entirely different like a robot as an [IoT](https://www.oracle.com/internet-of-things/what-is-iot/) device program and simulate eating behavior with `LivingThing`.

Going back into our pretend MMORPG game, bridges can be used to create more bridges. MMORPG usually have some profile page where users can edit their settings.

This `Profile` can itself utilize the Bridge Design Pattern to define a suite of pieces to make it function like a profile api:

```js
let key = 0

class Profile {
  constructor({ avatar, character, gender, username }) {
    this.character = null // Bridge
    this.gender = null
    this.username = username
    this.id = ++key
  }

  setCharacter(value) {
    this.character = value
    return this
  }

  setGender(value) {
    this.gender = value
    if (value === 'female') {
      this.showRecommendedEquipments('female')
    } else {
      this.showRecommendedEquipments('male')
    }
    return this
  }

  setUsername(value) {
    this.username = value
    return this
  }

  showRecommendedEquipments() {
    // Do something with this.character
  }

  save() {
    return fetch(`https://some-database-endpoint.com/v1/profile/${key}`, {
      method: 'POST',
      body: JSON.stringify({
        character: this.character,
        gender: this.gender,
        username: this.username,
      }),
    })
  }
}
```

If you've read some of my other articles this might feel similar to the Adapter or Strategy pattern.

There are distinct differences that solve different problems however:

In the Adapter pattern the problem it solves starts from the code (or _prior_ to runtime) where we would construct the Adapter first then immediately start with the rest:

[axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter)

```js
function adapter() {
  return function (config) {
    var mockAdapter = this
    // axios >= 0.13.0 only passes the config and expects a promise to be
    // returned. axios < 0.13.0 passes (config, resolve, reject).
    if (arguments.length === 3) {
      handleRequest(mockAdapter, arguments[0], arguments[1], arguments[2])
    } else {
      return new Promise(function (resolve, reject) {
        handleRequest(mockAdapter, resolve, reject, config)
      })
    }
  }.bind(this)
}
```

Compare that with our earlier snippets of _twilio-video_ and you will feel the difference immediately.

## 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!