This is in continuation to the previous post. Please note that this post will be a long one, so if you have time, keep reading.
Bridge Pattern
What
It states that “Separating Object’s interface from its actual implementation”.
What the above line means is that, usually OO developers try to keep a clean separation between the interface and its implementation. This allows us to write flexible code throughout our complex systems ensuring the link between the components are not too strong so that decoupling them or adding/modifying the same component in the future is not tedious.
As the name itself says, it acts as bridge between components in a complex system. The specialty of this bridge is that more number of components can be plugged into this bridge and made to work, without modifying or altering any component. Only the bridge needs a bit of alterations which is much more efficient than modifying other connected components.
Now I am sure, you shall be in a state of confusion that it almost sounds like Adapter pattern that I explained earlier. I would say just hold your doubts/curiosity for now, since my last post on this series will talk about the same.
Why
As per the above said lines and by its definition, it acts as a bridge. Hence, it's very much helpful for us in:
- Connecting many different components together, where in the implementation and abstraction/interface can vary independently. In many real world situations, we shall encounter situations like many components are getting developed or undergoing frequent changes in itself due to many reasons. In such cases, coupling them together and building systems are really a tedious process. Hence, to ease the process of coupling these independently varying components in a better way, this pattern is used.
- Since we aimed at loose coupling the interface/abstraction with its implementation, we gain a big leap in advantage with respect to hard binding. Basically, in modern-day programming, the components need to be more flexible in nature. And it should have very less dependency as well. So loose binding is a way to go.
- Since the components vary independently, if this pattern wasn’t used, then we would have gone for long inheritance tree implementation in order to have features supported in the components which vary independently at any time.
How
Let's take a look at the example of drawing a rectangle for a Normal window or for a dialog box. Basically, all window frames which need to be displayed on-screen are in rectangle shape (at least by default). Now assume that I have few different platforms wherein I need to display this rectangle box based on which type of window. But unfortunately, the platforms are very different in nature, so the way in which I am supposed to draw the rectangle for these window types is also quite different. At a later point of time, more number of platforms, viz., Linux, Windows, Mac, etc. could be added or even more number of window types also could be added.
So let's take a look at the code now:
As per the above code, I have created a base class which has few data members in the base class which is abstract. Now since I have many window types, hence there are many sub classes as per the window type, viz. NormalWindow
, DialogWindow
, etc.
If you see the code, the sub classes ctor
takes a parameter which specifies which platform specific frame should be drawn. These subclasses also override DrawRectangle()
method since the way the window frame has to be drawn is different.
Now I have pushed the creation of platform specific object to the abstract
class method GetOSTypeObject()
, so that multiple implementation is not required in sub classes. Yes, I could have made this method sealed
, but that’s left to the scenario in your application.
You could argue that any new requirement for a window type needs alteration to this class. But hey, is it not Open Closed principle we are following? But yes, there is a bit of modification required if there is a new platforms are added, viz. OS2, WebOS, BSDUnix, Solaris Unix, etc. But I can say that you can solve that too by using another pattern here, perhaps abstract factory. By doing so, you shall end up in a big soup called Patterns of Patterns.
As said already, too much of pattern fever shall make you go crazy and make your code more complex. So try to be a bit soft on this, after all I think patterns are like sexy girls, the more you think about them, the more you get addicted to them
Let's look at the other side of the component which also gets changed frequently, viz. Platforms.
As this phase of the component too, I am having many subclasses for each specific platforms. One thing you need to observe here is that the implementation details are hidden from everyone else. The requester object doesn’t even know how actually the line is drawn by the platform it is asking. So as already said at run time, every object is just delegating a request call to an unknown object which does some job and gets the result back.
Now let's see the usage code part:
I think the usage code is self-explanatory.
Where
There are many examples in real world usage:
- In House, Electric appliances vs Switch. Wherein you can find many appliances which varies like fans, TV, refrigerators, AC, etc. but at the same time, you switch on them using a switch which also varies in many shapes although their functionality is the same. These switches can come in Toggle, up and down, illuminated, dimmer, etc.
- Similar to the above code example I gave out, you can also use this pattern in representing Text file storage in various platforms. Because each platform's handle text in a different way like
char
to end a line, format of the text storage, compression used if any, etc. In the same way, the type of text files such as normal simple text files, RTF, etc.
Well, that’s all I can talk about this pattern. I am sorry if it’s too lengthy, but I wanted to make every point clear.
Look out for my next post in continuation to this.
Thanks for reading.
P.S.: Your valuable comments, votes are well appreciated.