- The technical support team acts as an interface to the product whereas the development team implements the product. Separating the technical support team from the development team is similar to isolating the abstraction from implementation. That's what the Bridge Pattern is intended to do.
- CImage maintains a reference to the CImageImp object. When the client calls Load or Show method in CImage, it does some preprocessing and forwards the request to CImageImp object by calling InitImageInfo or PaintImage method that provides the actual implementation. Isolating the image and image implementation in separate class hierarchies entitles them to vary independently. UML diagram showing the relationship between the participants of the Bridge Pattern is presented below.
Benefits in using Bridge Pattern
1.Decoupling abstraction from implementation - Inheritance tightly couples an abstraction with an implementation at compile time. Bridge pattern can be used to avoid the binding between abstraction and implementation and to select the implementation at run time.
2.Reduction in the number of sub classes - Sometimes, using pure inheritance will increase the number of sub classes. Let us assume that the full-blown version of our Image Viewer supports 6 image formats in 3 different operating systems. Pure inheritance would have resulted in 18 sub classes whereas applying Bridge Pattern reduces the sub class requirement only to 9.
3.Cleaner code and Reduction in executable size - In the above example, operating system specific code is encapsulated in CImageImp sub classes. This results in a cleaner code without much preprocessor statements like #ifdefs, #ifndefs. Also, it is easy to conditionally compile CImageImp sub classes for a given operating system to reduce the size of the executable.
4.Interface and implementation can be varied independently - Maintaining two different class hierarchies for interface and implementation entitles to vary one independent of the other.
5.Improved Extensibility - Abstraction and implementation can be extended independently. As mentioned earlier, the above example can easily be extended to view other image formats on Windows or view BMP images on other operating systems.
6.Loosely coupled client code - Abstraction separates the client code from the implementation. So, the implementation can be changed without affecting the client code and the client code need not be compiled when the implementation changes. (NOTE : In the above mentioned example, for the sake of simplicity, the application configures the CImage object with the right CImageImp object. However, alternate methods like Abstract Factory can be adopted to choose the CImageImp object.)
Drawbacks in using Bridge Pattern
1. Double indirection - In the above example, operating system specific methods are implemented by subclasses of CImageImp class. CImage class must delegate the message to a CImageImp subclass which implements the appropriate method. This will have a slight impact on performance.
Bridge allows two implementations to vary independently.
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
class TimeImp {
public:
TimeImp(int hr, int min) {
hr_ = hr;
min_ = min;
}
virtual void tell() {
cout << "time is " << hr_ << min_ << endl;
}
protected:
int hr_, min_;
};
class CivilianTimeImp: public TimeImp {
public:
CivilianTimeImp(int hr, int min, int pm): TimeImp(hr, min) {
if (pm)
strcpy(whichM_, " PM");
else
strcpy(whichM_, " AM");
}
/* virtual */
void tell() {
cout << "time is " << hr_ << ":" << min_ << whichM_ << endl;
}
protected:
char whichM_[4];
};
class ZuluTimeImp: public TimeImp {
public:
ZuluTimeImp(int hr, int min, int zone): TimeImp(hr, min) {
if (zone == 5)
strcpy(zone_, " Eastern Standard Time");
else if (zone == 6)
strcpy(zone_, " Central Standard Time");
}
/* virtual */
void tell() {
cout << "time is " << hr_ << min_ << zone_ <<
endl;
}
protected:
char zone_[30];
};
class Time {
public:
Time(){}
Time(int hr, int min) {
imp_ = new TimeImp(hr, min);
}
virtual void tell() {
imp_->tell();
}
protected:
TimeImp *imp_;
};
class CivilianTime: public Time {
public:
CivilianTime(int hr, int min, int pm) {
imp_ = new CivilianTimeImp(hr, min, pm);
}
};
class ZuluTime: public Time {
public:
ZuluTime(int hr, int min, int zone) {
imp_ = new ZuluTimeImp(hr, min, zone);
}
};
int main() {
Time *times[3];
times[0] = new Time(14, 30);
times[1] = new CivilianTime(2, 30, 1);
times[2] = new ZuluTime(14, 30, 6);
for (int i = 0; i < 3; i++)
times[i]->tell();
}
//Output
//time is 1430
//time is 2:30 PM
//time is 1430 Central Standard Time