The Bridge Design pattern is one of the design patterns I’ve had most difficulty wrapping my head around. 🤯 Note: This article assumes some basic knowledge of Interfaces in Object Oriented Programming
In this article, I’ll explain what this pattern is, how it can be used and an example of where it is currently used in the Frontend space (psst Angular 🚀).
We’ll go over a few things:
- What is it? 🤔
- Let’s break it down 😱
- But why? 😐
- Where can I find it in action? 🚀
- Where else could I use it? 🎉
What is it? 🤔
The Bridge Pattern can be thought of as part if the Composition over Inheritance Argument. It essentially “bridges a gap” between the Abstraction and the Implementation. These terms might cause a little confusion so lets clear them up.
- Implementation — This is an interface that will describe a set of specific behaviour that can be used by any Object in our codebase. It can have multiple concrete impletations that adhere to the contract defined in the interface.
- Abstraction — This is the object that will provide an API that will make use of an underlying Implementation. It acts as a layer over the top of the Implementation that can be further refined through inheritance if required.
Ok, mind blown. 🤯 I know, that can be a bit hairy to read.
So let’s take a quick look at a UML Diagram (I know, sigh, but it really does help):
As we can see from the diagram, the pattern allows us to separate two interfaces that can define the specifics of an object, in this case the type of Shape the object is and the Color that Shape is.
We can create multiple colors, or multiple shapes, without worrying about one impacting the other, therefore increasing loose coupling in our codebase! 🔥
Let’s break it down 😱
Keeping our example above in mind, the Abstraction would be our Shape class, whilst our Implementation is the Color class.
The Abstraction contains a reference to the Implementation via a property on the class (hence the composition over inheritance), in our case, Shape has a Color property. Any class that implements the Color contract can then be used by any Shape property.
The consumer of the Abstraction doesn’t need to worry about the underlying implementation whilst the pattern itself also increases loose coupling between the Abstraction and the Implementation.
If you’re like me, looking at code can help clear things up. So let’s do just that!
We’ll be using TypeScript for these examples
Awesome! We can create as many
Colors as we like, or as many
Shapes as we like, without affecting one or the other! 🚀🚀🚀
Keeping things simple and separated increases the maintainability and testability of our code, which leads to better code! We can extend and new shapes and colors in the future very easily now too!
But why? 😐
Let’s take a look at some reasons why we would use this pattern:
- The Bridge Pattern decouples the Abstraction and the Implementation, therefore allowing the two to differ independently.
- It keeps the Abstraction and the Implementation in it’s their own inheritance hierarchy, allowing them to grow without affecting the other.
- The Abstraction does not need to know about the Concrete Implementation and therefore it can be set or swapped at run-time without breaking the Abstraction.
Awesome, but where can I use it? 🤔
Where can I find it in action? 🚀
Ok, the Bridge Pattern is awesome. It can increase our loose coupling, but, where can we actually use it? Where is it being used in the wild?
Angular uses it! (Big thanks to Wes Copeland for pointing this out to me.)
ControlValueAccessor is an interface with methods that must be implemented for any class that implements it. Angular provides it's own concreate implementations of the
ControlValueAccessor Implementation, but any developer can implement the interface and any
NgControl will be able to use it!
In other words, an implementation outside of the Angular Framework is perfectly acceptable by an Abstraction within the Framework! 🔥🔥
Likewise, a developer can create their own
NgControl and any of the concrete implementations provided by Angular will be able to work with it! 💥💥
Hopefully that can help you understand the power behind The Bridge Pattern, but if you still need your own use case, keep reading!
Where else could I use it? 🚀
Well, I’ve found that a perfect example of this in the Frontend world would be in a Data Access Layer.
You can have the following:
- An Abstraction defining an Entity Service that will handle logic related to the entities within your system.
- An Implementation defining an API Interface that allows you to interact with any potential backend system or API.
Let’s take a quick look at this in action:
We’ll start with our Implementation (API Interface):
and next we will define our Abstraction (Entity Service):
Ok, so we’ve set up our Abstraction and our Implementation. Let’s put them to use!
First let’s create a UserService that refines our Abstraction.
Now that we have a Refined Abstraction, let’s go ahead and create a Concrete Implementation
Ok, now let’s use our Concrete Implementation along with our Refined Abstraction:
UserService does not need to know the nitty gritties of the
IApiService Implementation for it to still perform as expected.
What happens if down the road requirments change and suddenly we can’t use the
CustomApiService any more! 😱
Have no fear, the Bridge Pattern is here! 😍
Simply create a new Concrete Implementation, and supply that to the
Isn’t that awesome! 🚀🚀🚀
Hopefully you learned a bit ( more?) about the Bridge Patterb from this article, a potential use-case for it and how it is used in Angular.
If you have any questions, feel free to ask below or reach out to me on Twitter: @FerryColum.
Originally published at https://dev.to on December 4, 2019.