Bridge Design Pattern
Video Lecture
Section | Video Links |
---|---|
Bridge Pattern | |
Bridge Use Case |
Overview
The Bridge pattern is similar to the Adapter except in the intent that you developed it.
The Bridge is an approach to refactor already existing code, whereas the Adapter creates an interface on top of existing code through existing available means without refactoring any existing code or interfaces.
The motivation for converting your code to the Bridge pattern is that it may be tightly coupled. There is logic and abstraction close together that is limiting your choices in how you can extend your solution in the way that you need.
E.g., you may have one Car class, that produces a very nice car.
const CAR = new Car()
> Car has wheels and engine and windows and everything else.
But you would like to delegate the engine dynamically from a separate set of classes or solutions.
const ENGINE = new EngineA()
const CAR = new Car(ENGINE)
The Bridge pattern is a process about separating abstraction and implementation, so this will allow you more ways of using your classes.
A Bridge didn't exist before, but since after the separation of interface and logic, each side can be extended independently of each other.
The Bridge pattern should use composition instead of inheritance. This means that you assign the relationship when the object is created at runtime rather than hard coded in the class definition.
I.e., CAR = new Car(EngineA)
rather than class Car extends EngineA
A Bridge implementation will generally be cleaner than an Adapter solution that was bolted on. Since it involved refactoring existing code, rather than layering on top of legacy or third-party solutions that may not have been intended for your particular use case.
Terminology
- Abstraction Interface: An interface implemented by the refined abstraction describing the common methods to implement.
- Refined Abstraction: A refinement of an idea into another class or two. The classes should implement the Abstraction Interface and assign which concrete implementer.
- Implementer Interface: The implementer interface that concrete implementers implement.
- Concrete Implementer: The implementation logic that the refined abstraction will use.
Bridge UML Diagram
Source Code
In the concept demonstration code, imagine that the classes were tightly coupled. The concrete class would print out some text to the console.
After abstracting the class along a common ground, it is now more versatile. The implementation has been separated from the abstraction, and now it can print out the same text in two different ways.
The benefit now is that each refined abstraction and implementer can now be worked on independently without affecting the other implementations.
./src/bridge/bridge-concept.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Output
node ./dist/bridge/bridge-concept.js
[ 'a', 'b', 'c' ]
a
b
c
SBCODE Editor
Bridge Use Case
In this example, I draw a square and a circle. Both of these can be categorized as shapes.
The shape is set up as the abstraction interface. The refined abstractions, Square
and Circle
, implement the IShape
interface.
When the Square and Circle objects are created, they are also assigned their appropriate implementers being SquareImplementer
and CircleImplementer
.
When each shape's draw
method is called, the equivalent method within their implementer is called.
The Square and Circle are bridged and each implementer and abstraction can be worked on independently.
Example UML Diagram
Source Code
./src/bridge/client.ts
1 2 3 4 5 6 7 8 9 10 11 12 |
|
./src/bridge/circle-implementer.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
./src/bridge/square-implementer.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
./src/bridge/circle.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
./src/bridge/square.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
./src/bridge/ishape-implementer.ts
1 2 3 4 5 |
|
./src/bridge/ishape.ts
1 2 3 4 5 |
|
Output
node ./dist/bridge/client.js
******
** **
* *
* *
* *
* *
** **
******
**************
* *
* *
* *
* *
* *
* *
**************
SBCODE Editor
Summary
- Use when you want to separate a solution where the abstraction and implementation may be tightly coupled, and you want to break it up into smaller conceptual parts.
- Once you have added the bridge abstraction, you should be able to extend each side of it separately without breaking the other.
- Also, once the bridge abstraction exists, you can more easily create extra concrete implementations for other similar products that may also happen to be split across similar conceptual lines.
- The Bridge pattern is similar to the adapter pattern except in the intent that you developed it. The bridge is an approach to refactor already existing code, whereas the adapter adapts to the existing code through its existing interfaces and methods without changing the internals.