Bridge pattern lets you split a big class into two separate hierachies, abstraction and implementation so that the two can vary independently.
- An abstraction and its implementation should be defined and extended independently from each other.
- A compile-time binding between an abstraction and its implementation should be avoided so that an implementation can be selected at run-time.
When using subclassing, different subclasses implement an abstract class in different ways (eg. Shape <- Sqaure <- BlueSquare/RedSquare
). But an implementation is bound to the abstraction at compile-time and can't be changed at run-time.
The Bridge pattern attempts to solve it by replacing inheritance with delegations.
- Separate an abstraction from its implementation by putting them in separate class hierarchies.
- Implement the abstraction in terms of (by delegating to) an
Implementor
object.
This enables to configure an Abstraction
(Interface) with an Implementor
(Interface) object at run-time.
- Decouples an implementation so that it is not bound permanently to an interface.
- Abstraction and implementation can be extended independently.
- Changes to the concrete abstraction classes don't affect the client.
- Allows building platform independent code
- Hides the implementation details from client
- Increases overall code complexity by creating multiple additional classes.
- Useful in graphic and windowing systems that need to run over multiple platforms
- Useful any time you need to vary an interface and an implementation in different ways
- Abstraction
- defines the abstraction's interface
- maintains a reference to an object of type
Implementor
.
- RefinedAbstraction
- extends the interface defined by
Abstraction
.
- extends the interface defined by
- Implementor
- defines the interface for implementation class. This interface doesn't have to correspond exactly to the
Abstraction
's interface. Typically the implementation interface provides only primitive operations, and Abstraction defines higher-level operations based on these primitives.
- defines the interface for implementation class. This interface doesn't have to correspond exactly to the
- ConcreteImplementor
- implements the
Implementor
interface.
- implements the
Usage
// Abstraction-Implementation combination 1 => Smart TV + Physical Remote
var smartTv = new SmartTv();
var physicalRemote = new PhysicalRemote(smartTv);
physicalRemote.PowerOn();
physicalRemote.VolumeUp();
physicalRemote.PowerOff();
// Abstraction-Implementation combination 2 => Smart TV + Smart App Remote
var smartAppRemote = new SmartAppRemote(smartTv);
smartAppRemote.PowerOn();
smartAppRemote.VolumeUp();
smartAppRemote.PowerOff();
// Abstraction-Implementation combination 3 => Normal TV + Physical Remote
var normalTv = new NormalTv();
var physicalRemote2 = new PhysicalRemote(normalTv);
physicalRemote2.PowerOn();
physicalRemote2.VolumeUp();
physicalRemote2.PowerOff();
- Adapter makes the unrelated classes work together. Adapter makes things work after they're designed; Bridge is designed beforehand to let the abstraction and implmentation vary independently. [GoF, p219]