The Bridge design pattern is used to "decouple an abstraction from it's implementation so that the two can vary independently".
- defines the abstract interface
- maintains the Implementor reference
- extends the interface defined by Abstraction
- defines the interface for implementation classes
- implements the Implementor interface
Example: Shape abstraction
When the abstraction and implementation of drawing a shape are separated, they can vary independently. There are many types of shapes, such as circles, triangles and rectangles, each with its own properties. There is one thing all shapes do and that's drawing themselves. However, drawing shapes (graphics) to a screen can sometimes be dependent on different graphics implementations. For example in Java you have two different Graphics APIs, Swing and AWT.
Say you want to be able to draw your shape with both APIs, but you don't want each shape to implement each API or modify them so they are able to use different APIs. That's where the bridge pattern comes into play. The bridge pattern helps by allowing the creation of new implementation classes that provide the drawing implementation. The abstraction class, shape, provides a bridge to the implementation class, ShapeImp. The subclasses of the Shape class, Circle and Triangle, are then able to draw themselves by accessing the operations provided by ShapeImp. The operations provided by ShapeImp are then implemented by its subclasses using the desired API.
If a new shape needs to be created or there is a new graphics API to be drawn on, then it is very easy to add a new implementation class that implements the needed features.
You should use the Bridge pattern when:
- you don't want to bind an abstraction to its implementation, for example because you want to be able to switch the implementation at runtime.
- you may extend both the abstraction and the implementation through subclassing and you want to be able to combine the different abstractions and implementations independently.
- changes in the implementation should not affect clients and force them to be recompiled.
- you have too many classes in a class hierarchy and want to split it into two.
- you want to share an implementation between several objects without clients knowing about it.
- A implementation is not bound to an interface and vice versa. This gives you extra flexibility and means that an object can change its implementation at runtime.
- Improved extensibility because you can extend abstractions and implementations independently.
- Implementation details (like implementation sharing) are hidden from clients.
- Abstract Factory: This pattern can be useful for creating and configuring bridges.
- Adapter: The Adapter pattern looks similar but has a different intent. It aims to make unrelated classes work together, unlike Bridge which decouples abstractions and implementations.
Creational: Abstract Factory | Builder | Factory Method | Prototype | Singleton