This is the 13th post in a series on design patterns.
Bridge is a structural design pattern that lets you split a large class or a set of closely related classes into two separate hierarchies — abstraction and implementation — which can be developed independently of each other.
Imagine you have a Shape class with two sub-classes, Circle and Square. This class hierarchy needs to be extended to include colors, so you plan to create Red and Blue shape subclasses. Due to the fact that you already have two subclasses, you will have to create four class combinations, like BlueCircle and RedCircle. The hierarchy will grow exponentially when new shapes and colors are added.
We run into this problem because we are trying to extend shape classes in two dimensions, form and color.
Bridge Design Pattern
Bridge pattern tries to solve this problem by switching from inheritance to composition. It means that you extract one dimension into a separate class hierarchy so that the original classes will reference an object in the new hierarchy instead of having the entire state and behavior contained in one class.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
- Identify the orthogonal dimensions of your classes. These independent concepts are abstraction/platform, domain/infrastructure, front-end/back-end, or interface/implementation.
- Find out what operations the client needs and define them in the base abstraction class.
- Determine what operations are available on all platforms. Include the ones the abstraction needs in the general implementation interface.
- Create concrete implementation classes for all platforms in your domain, but ensure they all follow the implementation interface.
- Add a reference field for the implementation type to the abstraction class. The abstraction delegates most of the work to the implementation object that’s referenced in that field.
- Create refined abstractions for each variant of high-level logic by extending the base abstraction class.
- An independent object should be passed to the abstraction’s constructor in order to associate one with the other. Afterward, the client can forget about the implementation and work only with the abstraction object.
Source Code Implementation
The Shape abstract class provides high-level control logic. The actual low-level work is done by the Color object.
A Color defines the interface that’s common to all concrete implementations of RedColor and BlueColor. A Shape can only communicate with a Color object through methods declared here.
BlueColor is the concrete implementation of the Color interface.
RedColor is the concrete implementation of the Color interface.
The Circle class extends the Shape Class and invokes the Color method.
The Square class extends the Shape Class and invokes the Color method.
BridgeClient only works with the abstract class Shape. Here, it is the client’s responsibility to link the abstraction object (Shape) with one of the implementation objects (Color).
Filling class com.learncsdesign.Circle
Filling class com.learncsdesign.Square
When To Apply Bridge Design Pattern
- If you need to divide and organize a monolithic class that has several variants of some functionality, use the Bridge pattern. Using the Bridge pattern, you can split a monolithic class into several hierarchical classes. As a result, you can change the classes in each hierarchy independently of the others. It simplifies code maintenance and minimizes the risk of breaking existing code.
- The pattern can be used when you need to extend a class in several orthogonal (independent) dimensions.
- If you need to switch between implementations at runtime, use the Bridge. It’s optional, but the Bridge pattern lets you replace the implementation object inside the abstraction. By the way, this last item is the main reason why so many people confuse the Bridge pattern with the Strategy pattern.
Pros of Bridge Design Pattern
- Apps and classes can be created that are platform-independent.
- Client code is based on high-level abstractions.
- You can introduce new abstractions and implementations independently.
- It is possible to focus on high-level logic in the abstraction and on platform details in the implementation.