Bridge Pattern Tutorial with Java Examples
Today's pattern is the Bridge pattern, which allows you to vary both the implementation and the abstraction by placing both in seperate class hierachies.
Bridge in the Real World
The display of different image formats on different operating systems is a good example of the Bridge pattern. You might have different image abstractions for both jpeg and png images. The image structure is the same across all operating systems, but the how it's viewed (the implementation) is different on each OS. This is the type of decoupling that the Bridge pattern allows.
Design Patterns Refcard
For a great overview of the most popular design patterns, DZone's Design Patterns Refcard is the best place to start.
The Bridge Pattern
The Bridge pattern is known as a structural pattern,as it's used to form large object structures across many disparate objects. Thedefinition of Bridge provided in the original Gang of Four book on DesignPatterns states:
Decouple an abstraction from its implementation so that the two can vary independently
Let's take a look at the diagram definition before we go into more detail.
- The Abstraction defines the abstraction, and maintains the reference to the implementor. RefinedAbstraction provides an extension to the Abstraction, usually adding extra methods that provide different ways of getting at the same functionality. The Implementor interface defines an interface for the implementation classes (the ConcreateImplementor classes).
-
RefinedAbstractions are implemented in terms of the abstraction, and not that implementation interface.This means that the implementation details are hidden from the client. The pattern is similar to the Adapter pattern, except the Bridge pattern separates the interface from implementation.
Would I Use This Pattern?
The Bridge pattern should be used when both the class as well as what it does vary often. The bridge pattern can also be thought of as two layers of abstraction. When the abstractions and implementations should not be bound at compile time, and should be independently extensible the pattern should be used.
In particular this pattern is useful in graphic toolkits that need to run on multiple platforms. You'll see this in AWT, where a component has a component peer which does the OS specific operations. Also the Collections framework has examples of the bridge interface: ArrayList and LinkedList are implement List. And List provides common methods to add, remove or check size.
So How Does It Work In Java?
Here's the pattern in action using the remote control example from Head First Design Patterns.
First, we have our TV implementation interface:
//Implementor public interface TV{public void on();public void off(); public void tuneChannel(int channel);}
And then we create two specific implementations - one for Sony and one for Philips:
//Concrete Implementor public class Sony implements TV{public void on(){//Sony specific on}public void off(){//Sony specific off}public void tuneChannel(int channel);{//Sony specific tuneChannel}}//Concrete Implementor public class Philips implements TV{public void on(){//Philips specific on}public void off(){//Philips specific off}public void tuneChannel(int channel);{//Philips specific tuneChannel}}
These classes deal with the specific implementations of the TV from each vendor.
Now, we create a remote control abstraction to control the TV:
//Abstractionpublic abstract class RemoteControl{ private TV implementor; public void on() { implementor.on(); } public void off() { implementor.off(); } public void setChannel(int channel) { implementor.tuneChannel(channel); }}
As the remote control holds a reference to the TV, it can delegates the methods through to the interface. But what is we want a more specific remote control - one that has the + / - buttons for moving through the channels? All we need to do is extend our RemoteControl abstraction to contain these concepts:
//Refined abstractionpublic class ConcreteRemote extends RemoteControl{ private int currentChannel; public void nextChannel() { currentChannel++; setChannel(currentChannel); } public void prevChannel() { currentChannel--; setChannel(currentChannel); } }
Watch Out for the Downsides
One of the major drawbacks of this pattern is that, in providing flexibility, it increases complexity. There's also possible performance issues with the indirection of messages - the abstraction needs to pass messages along to the implementator for the operation to get executed.
Next Up
More patterns later this week - we're close to the end now!
Enjoy the Whole "Design Patterns Uncovered" Series:
Creational Patterns
- Learn The Abstract Factory Pattern
- Learn The Builder Pattern
- Learn The Factory Method Pattern
- Learn The Prototype Pattern
Structural Patterns
- Learn The Adapter Pattern
- Learn The Bridge Pattern
- Learn The Decorator Pattern
- Learn The Facade Pattern
- Learn The Proxy Pattern
Behavioral Patterns
- Learn The Chain of Responsibility Pattern
- Learn The Command Pattern
- Learn The Interpreter Pattern
- Learn The Iterator Pattern
- Learn The Mediator Pattern
- Learn The Memento Pattern
- Learn The Observer Pattern
- Learn The State Pattern
- Learn The Strategy Pattern
- Learn The Template Method Pattern
- Learn The Visitor Pattern