1. 问题
总结面向对象实际上就两句话:一是松耦合(Coupling),二是高内聚(Cohesion)。面向对象系统追求的目标就是尽可能地提高系统模块内部的内聚(Cohesion)、尽可能降低模块间的耦合(Coupling)。然而这也是面向对象设计过程中最为难把握的部分,大家肯定在OO系统的开发过程中遇到这样的问题:
1)客户给了你一个需求,于是使用一个类来实现(A);
2)客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1和A2);
3)客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00和A01,其中A00表示原来的类A)实现不同操作系统上的客户需求,这样我们就有了一共4个类。
4)可能用户的需求又有变化,比如说又有了一种新的算法……..
5)我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。
Bridge模式则正是解决了这类问题。
2. 模式选择
Bridge模式典型的结构图为:
在Bridge模式的结构图中可以看到,系统被分为两个相对独立的部分,左边是抽象部分,右边是实现部分,这两个部分可以互相独立地进行修改:例如上面问题中的客户需求变化,当用户需求需要从Abstraction派生一个具体子类时候,并不需要像上面通过继承方式实现时候需要添加子类A1和A2了。另外当上面问题中由于算法添加也只用改变右边实现(添加一个具体化子类),而右边不用在变化,也不用添加具体子类了。
一切都变得elegant!
/************************************************************************************************************/ * 作者:一雨田(http://blog.csdn.net/dylgsy/) * * Bridge模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。 * 这句话真是很晦涩,我来解释一下。所谓的实现部分,你可以想象成一个功能库,这个库是用类来组织的, * 并且实现了你需要的功能,而抽象部分就是使用这个库的类。 * 让他们独立变化的意思就是说,库增加或者使用库的代码改变都是在两边独立变化的,不影响另一边 * 就好像隔了个桥梁一样,如果你理解了,就会觉得BRIDGE这个名字改得真好。 * 好了,如果不明白也没关系,先看看下面的代码。应该能明白的 /************************************************************************************************************/ /************************************************************************************************************/ * 任务叙述:我们现在要实现一个画图系统,这个系统的需求暂时来说有以下几个: * 1、画圆、画长方形。(先实现画这两个形状) * 2、已经有两个画图库了,这些画图库中定义了我们需要的操作,我们没必要重新去实现了 * 3、我们的系统能够决定使用哪个的画图库中的操作 * 根据以上的需求,我们的代码设计如下(这里先不使用Bridge模式,以便对比) : /************************************************************************************************************/ /**********************************************************************************************************/ * 注意,这里使用了个预编译宏 _BRIDGE_MODE 来隔开使用Bridge和不使用Bridge两块代码。可以以这个来做分界对比看 * 两部分的代码 /**********************************************************************************************************/ #include <stdio.h> #include <iostream> using namespace std; // 把这里注释掉就是不使用Bridge模式的代码 #define _BRIDGE_MODE // 画图库1 class CDrawLib1 { public: void DrawCircle() { cout << "画圆操作1" << endl; } void DrawRectangle() { cout << "画长方形操作1" << endl; } void DrawTriangle() { cout << "画三角形操作1" << endl; } }; // 画图库2 class CDrawLib2 { public: void DrawCircle() { cout << "画圆操作2" << endl; } void DrawRectangle() { cout << "画长方形操作2" << endl; } void DrawTriangle() { cout << "画三角形操作2" << endl; } }; // 只要声明 _BRIDGE_MODE 就可以在使用或者不使用Bridge模式间切换了 #ifndef _BRIDGE_MODE//不使用桥模式代码 // 看到形状,很容易想到经典的Shape抽象类,并定义一个Draw接口 // 这里1代表使用画图库1的画图操作 class CShape1 { public: virtual void Draw() = 0; protected: CDrawLib1 _drawLib; }; // 画圆和画长方形,好,这里就派生出两个形状 class CCircle1: public CShape1 { public: // 实现Draw操作,使用画图库1 virtual void Draw() { // 使用画图库1 _drawLib.DrawCircle(); } }; class CRectangle1: public CShape1 { public: // 实现Draw操作,但是要使用画图库1 virtual void Draw() { // 使用画图库1 _drawLib.DrawRectangle(); } }; // 再定义使用画图库2的Shape class CShape2 { public: virtual void Draw() = 0; protected: CDrawLib2 _drawLib; }; // 画圆和画长方形,好,这里就派生出两个形状 class CCircle2: public CShape2 { public: // 实现Draw操作,使用画图库2 virtual void Draw() { // 使用画图库2 _drawLib.DrawCircle(); } }; class CRectangle2: public CShape2 { public: // 实现Draw操作,但是要使用画图库2 virtual void Draw() { // 使用画图库2 _drawLib.DrawRectangle(); } }; void Draw1(CShape1 &s) { s.Draw(); } void Draw2(CShape2 &s) { s.Draw(); } // 好了,我们开始使用上面的类来实现我们的画图系统 void main() { cout << "不使用Bridge模式" << endl; // 我可以使用两种库 CCircle1 c1; CCircle2 c2; CRectangle1 r1; CRectangle2 r2; Draw1(c1); Draw1(r1); Draw2(c2); Draw2(r2); } /************************************************************************************************************/ * 好的,上述的代码运行正常,如果不需要维护的话,我们就不用管它拉~~ * 但是,代码是一定要维护的,逃不过的宿命。 * 出现变化的地方可能是这样的: * 1、出现了第三个库 * 2、画图系统需要画三角形 * 这个时候,我们再看看要完成这两个变化我们需要作的修改,就会发现,我要晕了 * (当一个程序员要晕的时候,也就是BUG要出现的时候了) /************************************************************************************************************/ #else /************************************************************************************************************/ * 好了,现在让我们使用Bridge模式来实现上面的系统 * Bridge模式最重要是把表示和实现分开 /************************************************************************************************************/ // 建立一个实现操作的类:CShapeImp class CShapeImp { public: virtual void DrawCircle() = 0; virtual void DrawRectangle() = 0; }; class CShapeImp1: public CShapeImp { public: virtual void DrawCircle() { _dLib.DrawCircle(); } virtual void DrawRectangle() { _dLib.DrawRectangle(); } private: CDrawLib1 _dLib; }; class CShapeImp2: public CShapeImp { public: virtual void DrawCircle() { _dLib.DrawCircle(); } virtual void DrawRectangle() { _dLib.DrawRectangle(); } private: CDrawLib2 _dLib; }; class CShape { public: virtual void Draw() = 0; protected: CShapeImp *_sImp; }; class CCircle: public CShape { public: CCircle(CShapeImp *imp) { _sImp = imp; } virtual void Draw() { _sImp->DrawCircle(); } }; class CRectangle: public CShape { public: CRectangle(CShapeImp *imp) { _sImp = imp; } virtual void Draw() { _sImp->DrawRectangle(); } }; // 好了,我们利用上面的类结构来实现我们的画图系统 void main() { cout << "使用Bridge模式" << endl; CShapeImp1 sImp1; CShapeImp2 sImp2; // 使用画图库1 CCircle c1(&sImp1); CRectangle r1(&sImp1); c1.Draw(); r1.Draw(); // 使用画图库2 CCircle c2(&sImp2); CRectangle r2(&sImp2); c2.Draw(); r2.Draw(); } /************************************************************************************************************/ * 好的,上述的代码运行正常,并且维护也方便了,回想一下上面的两个新需求: * 1、出现了第三个库 * 2、画图系统需要画三角形 * 考虑一下,对于第一个需求,我们只需要再增加一个ShapeImp就可以了。 * 再看第二个需求,我们也是只需要派生一个CTriangle就可以了 * 可以看出来,变化不再造成混乱,只需要单独针对变化改动代码就行了。 * 也就是,变化被Bridge给分开了。 /************************************************************************************************************/ /************************************************************************************************************/ * 对于这个的模式学习,个人推荐自己写写代码,感受一下使用Bridge和不使用的前后差别, * 应该也会很快搞定它的。 /************************************************************************************************************/ #endif
#include <iostream> using namespace std; //手机软件 class HandsetSoft { public: virtual void Run()=0; }; //游戏软件 class HandsetGame : public HandsetSoft { public: virtual void Run() { cout<<"运行手机游戏"<<endl; } }; //通讯录软件 class HandSetAddressList : public HandsetSoft { public: virtual void Run() { cout<<"手机通讯录"<<endl; } }; //手机品牌 class HandsetBrand { protected: HandsetSoft* m_soft; public: void SetHandsetSoft(HandsetSoft* temp) { m_soft = temp; } virtual void Run()=0; }; //M品牌 class HandsetBrandM : public HandsetBrand { public: virtual void Run() { m_soft->Run(); } }; //N品牌 class HandsetBrandN : public HandsetBrand { public: virtual void Run() { m_soft->Run(); } }; //客户端 int main() { HandsetBrand *brand; brand = new HandsetBrandM(); brand->SetHandsetSoft(new HandsetGame()); brand->Run(); brand->SetHandsetSoft(new HandSetAddressList()); brand->Run(); return 0; }
使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。