What is the Bridge pattern?
The Bridge pattern is a structural design pattern. That is, its main task is to create a complete structure of classes and objects. Bridge solves this problem by separating one or more classes into separate hierarchies - abstraction and implementation . A change in functionality in one hierarchy does not entail changes in another. Everything seems clear, but in fact this definition sounds very broad and does not answer the main question: “What is the Bridge pattern?” I think this will be easier for you to figure out in practice. Let's immediately model a classic example for the Bridge pattern. We have an abstract classShape
that generally describes a geometric figure:
-
Shape.java
public abstract class Shape { public abstract void draw(); }
When we decide to add triangle and rectangle shapes, we will inherit from the class
Shape
: -
Rectangle.java:
public class Rectangle extends Shape { @Override public void draw() { System.out.println("Drawing rectangle"); } }
-
Triangle.java:
public class Triangle extends Shape { @Override public void draw() { System.out.println("Drawing triangle"); } }
draw()
. To have different implementations of the method draw()
, we need to create a class for each shape corresponding to a color. If there are three colors, then there are six classes: TriangleBlack
, TriangleGreen
, TriangleRed
, RectangleBlack
, RectangleGreen
and RectangleRed
. Six classes is not that big of a deal. But! If we need to add a new shape or color, the number of classes will grow exponentially. How to get out of this situation? Storing colors in a field and trying options through conditionals is not the best solution. A good solution is to display color in a separate interface . No sooner said than done: let's create an interface Color
and three of its implementations - BlackColor
, GreenColor
and RedColor
:
-
Color.java:
public interface Color { void fillColor(); }
-
BlackColor.java:
public class BlackColor implements Color { @Override public void fillColor() { System.out.println("Filling in black color"); } }
-
GreenColor.java
public class GreenColor implements Color { @Override public void fillColor() { System.out.println("Filling in green color"); } }
-
RedColor.java
public class RedColor implements Color { @Override public void fillColor() { System.out.println("Filling in red color"); } }
Now let's add a type field
Color
to the classShape
- we will receive its value in the constructor. -
Shape.java:
public abstract class Shape { protected Color color; public Shape(Color color) { this.color = color; } public abstract void draw(); }
color
We will use the variable in implementationsShape
. This means that shapes can now use the functionality of the interfaceColor
. -
Rectangle.java
public class Rectangle extends Shape { public Rectangle(Color color) { super(color); } @Override public void draw() { System.out.println("Drawing rectangle"); color.fillColor(); } }
Color color
is a bridge that interconnects two separate class hierarchies.
Bridge device: what is abstraction and implementation
Let's take a look at the class diagram that describes the Bridge pattern: Here you can see two independent structures that can be modified without affecting each other's functionality. In our case it is:- Abstraction - class
Shape
; - RefinedAbstraction - classes
Triangle
,Rectangle
; - Implementor - interface
Color
; - ConcreteImplementor - classes
BlackColor
,GreenColor
andRedColor
.
Shape
represents an Abstraction - a mechanism for controlling the coloring of shapes in different colors, which delegates the Implementation to the interface Color
. Classes Triangle
are Rectangle
real objects that use the mechanism offered by the class Shape
. BlackColor
, GreenColor
and RedColor
- specific implementations in the Implementation branch. They are often called a platform.
Where is the Bridge pattern used?
A huge advantage of using this pattern is that you can make changes to the functionality of classes in one branch without breaking the logic of another. This approach also helps to reduce the coupling of program classes. The main condition for using patterns is to “follow the instructions”: don’t stick them anywhere! Actually, let's figure out in what cases you definitely need to use Bridge:-
If it is necessary to expand the number of entities in two directions (geometric shapes, colors).
-
If you want to divide a large class that does not meet the Single responsibility principle into smaller classes with narrow-profile functionality.
-
If there is a possible need to make changes to the logic of the operation of certain entities while the program is running.
-
If necessary, hide the implementation from class (library) clients.
Pros and cons of the pattern
Like other patterns, the Bridge has both advantages and disadvantages. Benefits of Bridge:- Improves code scalability - you can add functionality without fear of breaking something in another part of the program.
- Reduces the number of subclasses - works when it is necessary to expand the number of entities in two directions (for example, the number of shapes and the number of colors).
- Makes it possible to work separately on two independent branches of Abstraction and Implementation - this can be done by two different developers without delving into the details of each other’s code.
- Reducing the coupling of classes - the only place where two classes are connected is the bridge (field
Color color
).
- Depending on the specific situation and the structure of the project as a whole, there may be a negative impact on program productivity (for example, if more objects need to be initialized).
- Complicates code readability due to the need to navigate between classes.
GO TO FULL VERSION