意图:
将抽象部分与实现部分分离,使它们都可以独立的变化。再通俗点说就是:实现系统可能有多角度的分类,每一种分类可能都有变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。
桥接模式结构图:
Abstraction
定义抽象的接口。该接口包含实现具体行为、具体特征的Implementor接口
Refined Abstraction
抽象接口Abstraction的子类,依旧是一个抽象的事物名
Implementor
定义具体行为、具体特征的应用接口
ConcreteImplementor
实现Implementor接口
例子:
大话设计模式上市手机和手机软件额例子,我觉得很不错,拿来参考。
可以从手机品牌的角度来分类:手机品牌作为基类,下面有品牌M和N两个子类,对于每个子类有通讯录,游戏等软件。对于这种分类如果要增加一个软件,就需要在么个品牌下面都增加一个子类,而且这些子类的区别并不大。
也可以从手机软件的角度来分类:手机软件最为基类,下面有通讯录,游戏等子类,对于每个子类又有M品牌通讯录,N品牌通讯录等等。对于这种分类,如果要增加一个手机品牌,也不是很方便。
有桥接模式的以地图可以看出,我们可以将抽象和实现相分离,即将手机品牌和手机软件相分离。结构图如下:
这样,无论是增加一个手机品牌还是增加一个手机软件都不需要写很多重复的子类代码。
采用合成/聚合复用原则的好处是:优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样类和类继承层次会保持较小的规模,并且不太可能增长为不可控制的庞然大物。
代码如下:
#include <iostream> using namespace std; class HandsetSoft { public: virtual void Run(){}; }; class HandsetGame : public HandsetSoft { public: virtual void Run() { cout<<"运行手机游戏"<<endl; } }; class HandsetAddressList : public HandsetSoft { public: virtual void Run() { cout<<"运行手机通讯录"<<endl; } }; class HandsetBrand { protected: HandsetSoft *soft; public: void SetHandsetSoft(HandsetSoft *soft) { this->soft = soft; } virtual void Run(){}; virtual ~HandsetBrand() { delete soft; } }; class HandsetBrandM : public HandsetBrand { virtual void Run() { soft->Run(); } }; class HandsetBrandN: public HandsetBrand { virtual void Run() { soft->Run(); } }; int main(int argc, char **argv) { HandsetBrand *ab; ab = new HandsetBrandN(); ab->SetHandsetSoft(new HandsetGame()); ab->Run(); ab->SetHandsetSoft(new HandsetAddressList()); ab->Run(); ab = new HandsetBrandM(); ab->SetHandsetSoft(new HandsetGame()); ab->Run(); ab->SetHandsetSoft(new HandsetAddressList()); ab->Run(); delete ab; system("pause"); return 0; }
模式优点:
分离抽象和实现部分
桥接模式分离了抽象部分和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。
更好的扩展性
由于桥接模式把抽象部分和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,从而大大地提高了系统的可扩展性。
可动态地切换实现
由于桥接模式把抽象部分和实现部分分离开了,所以在实现桥接的时候,就可以实现动态的选择和使用具体的实现。也就是说一个实现不再是固定的绑定在一个抽象接口上了,可以实现运行期间动态地切换。
可减少子类的个数
根据前面的讲述,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上的可变化数量的乘积个子类;而采用桥接模式来实现,大约需要两个纬度上的可变化数量的和个子类。可以明显地减少子类的个数。
桥接模式的缺点
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
适用环境
如果你不希望在抽象部分和实现部分采用固定的绑定关系,可以采用桥接模式,来把抽象部分和实现部分分开,然后在程序运行期间来动态地设置抽象部分需要用到的具体的实现,还可以动态地切换具体的实现。
如果出现抽象部分和实现部分都能够扩展的情况,可以采用桥接模式,让抽象部分和实现部分独立地变化,从而灵活地进行单独扩展,而不是搅在一起,扩展一边就会影响到另一边。
如果希望实现部分的修改不会对客户产生影响,可以采用桥接模式。由于客户是面向抽象的接口在运行,实现部分的修改可以独立于抽象部分,并不会对客户产生影响,也可以说对客户是透明的。
如果采用继承的实现方案,会导致产生很多子类,对于这种情况,可以考虑采用桥接模式,分析功能变化的原因,看看是否能分离成不同的纬度,然后通过桥接模式来分离它们,从而减少子类的数目