设计模式|设计模式 - Bridge 桥模式

Bridge桥模式也属于”的单一职责“模式中的典型模式。
问题描述:
我们绘制图形时,图形可以有不同形状以及不同颜色,比如圆形可以是红的,绿的,方形可以是红的绿的,如果用代码来描绘这些类,会有如下:

1 class Shape{ 2 }; 3 class Rectangle : public Shape{ 4 }; 5 class Circle : public Shape{ 6 }; 7 class Color{ 8 }; 9 class Red : public Color{ 10 }; 11 class Blue : public Color{ 12 }; 13 class RedRectangle : public Red{ 14 }; 15 class BlueRectangle : public Blue{ 16 };

每增加一种图形或者颜色,新增的类就会成倍得增长。而且CRedRectangle继承于颜色,似乎也不太合理,CRedRectangle和CRed之间不是一种is-a的关系。下面通过桥模式改善它?
定义
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。 ——《设计模式》GoF
简单的说就是抽象对外提供的接口,对外隐瞒实现部分,在抽象中引用实现部分,从而实现抽象对实现部分的调用,而抽象中引用的实现部分可以在今后的开发中,切换为别的实现部分。
动机
  • 解决继承带来的问题
    对象的继承关系是在编译时就定义好的,无法在运行时改变从父类继承的实现。父类实现中的任何变化都必然会导致子类发生变化。比如上面的代码,Red类是Color抽象类的具体实现,RedRectangle从Red类中继承了红色属性,就和颜色的实现绑定在了一起,RedRectangle的颜色实现就难以修改或扩展。通过桥接模式把依赖具体实现,提升为依赖抽象,来完成对象和变化因素之间的低耦合,提高系统的可维护性和扩展性。桥接模式的主要目的是将一个对象的变化与其它变化隔离开,让彼此之间的耦合度最低。
  • 合成/聚合复用原则
    聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
    绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案:
    方案一:为每一种形状都提供一套颜色版本
    设计模式|设计模式 - Bridge 桥模式
    文章图片

    方案二:根据实际需要对形状和颜色进行组合
    设计模式|设计模式 - Bridge 桥模式
    文章图片
    明显方案二采用聚合的方式可以减少很多类的数量。

UML类图
设计模式|设计模式 - Bridge 桥模式
文章图片



Abstraction类定义了抽象类的接口,并且维护一个指向Implementor实现类的指针;
RefineAbstraction类扩充了Abstraction类的接口;
Implementor类定义了实现类的接口,这个接口不一定要与Abstraction的接口完全一致;实际上,这两个接口可以完全不同;
ConcreteImplementor类实现了Implementor定义的接口。
代码实现
1 #include 2 3 using namespace std; 4 5 //具体实现的抽象 6 class Implementor { 7public: 8virtual void operatonImpl() = 0; 9 }; 10 11 //具体实现 12 class ConcreteImplementor : public Implementor { 13public: 14void oerationImpl() { cout << "OperationImpl" << endl; } 15 }; 16 17 class Abstruction { 18public: 19Abstruction(Implementor* pImpl) : m_pImpl(pImpl) {} 20virtual void operation() = 0; 21 22protected: 23Implementor* m_pImpl; 24 }; 25 26 //重新定义抽象 27 class RedfinedAbstraction : public Abstruction { 28public: 29RedfineAbstraction(Implementor* pImpl) : Abstruction(pImpl) {} 30void operation() { m_pImpl->operatonImpl(); } 31 }; 32 33 int main() { 34Implementor* pImplObj = new ConcreteImplementor(); 35Abstruction* pAbsObj = new RedfineAbstraction(pImplObj); 36pAbsObj->operation(); 37delete pImplObj; 38pImplObj = nullptr; 39delete pAbsObj; 40pAbsObj = nullptr; 41return 0; 42 }

使用桥模式重新实现形状与颜色的代码:
1 #include 2 #include 3 4 using namespace std; 5 6 class Color { 7public: 8virtual string name() = 0; 9 10protected: 11string mName; 12 }; 13 14 class Green : public Color { 15public: 16Green() { mName = "Green"; } 17virtual ~Green() {} 18virtual string name() { return mName; } 19 }; 20 21 class Red : public Color { 22public: 23Red() { mName = "Red"; } 24virtual ~Red() {} 25virtual string name() { return mName; } 26 }; 27 28 class Shape { 29public: 30Shape(Color* color) : mColor(color) {} 31virtual void myShape() = 0; 32 33protected: 34Color* mColor; 35 }; 36 37 class Rectangle : public Shape { 38public: 39Rectangle(Color* color) : Shape(color) {} 40virtual void myShape() { 41cout << "Rectangle has a " << mColor->name() << " color\n"; 42} 43 }; 44 45 class Circle : public Shape { 46public: 47Circle(Color* color) : Shape(color) {} 48virtual void myShape() { 49cout << "Circle has a " << mColor->name() << " color\n"; 50} 51 }; 52 53 int main() { 54Color* red = new Red(); 55Color* green = new Green(); 56 57Shape* rectangle = new Rectangle(red); 58rectangle->myShape(); 59Shape* circle = new Circle(green); 60circle->myShape(); 61 62return 0; 63 }


桥模式的优点
分离抽象接口及其实现的部分
  • 桥接模式有点类似于多继承,但是多继承违背了类的单一职责原则(一个类只有一个变化的原因),复用性差,且多继承结构中类的个数庞大,桥接模式是比多继承方案更好的解决方案。
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原系统。
  • 实现细节对客户透明,可以对用户隐藏实现细节。
桥模式的缺点
  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有局限性。

参考:
桥接模式(c++实现) C++设计模式——桥接模式 【设计模式|设计模式 - Bridge 桥模式】

    推荐阅读