C++桥接模式
<上一节
下一节>
由遇到的问题引出桥接模式
总结面向对象实际上就两句话:一是松耦合(Coupling),二是高内聚(Cohesion)。面向对象系统追求的目标就是尽可能地提高系统模块内部的内聚(Cohesion)、尽可能降低模块间的耦合(Coupling)。然而这也是面向对象设计过程中最为难把握的部分,大家肯定在OO 系统的开发过程中遇到这样的问题:- 客户给了你一个需求,于是使用一个类来实现(A);
- 客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1 和 A2);
- 客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00 和 A01,其中 A00 表示原来的类 A)实现不同操作系统上的客户需求,这样我们就有了一共 4 个类。
- 可能用户的需求又有变化,比如说又有了一种新的算法……..
- 我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。
桥接模式则正是解决了这类问题。
模式选择
桥接模式典型的结构图为:图 2-1:桥接模式结构图
在桥接模式的结构图中可以看到,系统被分为两个相对独立的部分,左边是抽象部分,右边是实现部分,这两个部分可以互相独立地进行修改:例如上面问题中的客户需求变化,当用户需求需要从 Abstraction 派生一个具体子类时候,并不需要像上面通过继承方式实现时候需要添加子类 A1 和 A2 了。另外当上面问题中由于算法添加也只用改变右边实现(添加一个具体化子类),而右边不用在变化,也不用添加具体子类了。
一切都变得 elegant!
桥接模式的实现
完整代码示例(code):桥接模式的实现起来并不是特别困难,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 VC 6.0 下测试运行)。代码片断 1:Abstraction.h
//Abstraction.h
#ifndef _ABSTRACTION_H_
#define _ABSTRACTION_H_
class AbstractionImp;
class Abstraction{
public:
virtual ~Abstraction();
virtual void Operation() = 0;
protected:
Abstraction();
private:
};
class RefinedAbstraction:public Abstraction{
public:
RefinedAbstraction(AbstractionImp* imp);
~RefinedAbstraction();
void Operation();
protected:
private:
AbstractionImp* _imp;
};
#endif //~_ABSTRACTION_H_
代码片断 2:Abstraction.cpp
//Abstraction.cpp
#include "Abstraction.h"
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
Abstraction::Abstraction(){
}
Abstraction::~Abstraction(){
}
RefinedAbstraction::RefinedAbstraction(AbstractionImp* imp){
_imp = imp;
}
RefinedAbstraction::~RefinedAbstraction(){
}
void RefinedAbstraction::Operation(){
_imp->Operation();
}
代码片断 3:AbstractionImp.h
//AbstractionImp.h
#ifndef _ABSTRACTIONIMP_H_
#define _ABSTRACTIONIMP_H_
class AbstractionImp{
public:
virtual ~AbstractionImp();
virtual void Operation() = 0;
protected:
AbstractionImp();
private:
};
class ConcreteAbstractionImpA:public AbstractionImp{
public:
ConcreteAbstractionImpA();
~ConcreteAbstractionImpA();
virtual void Operation();
protected:
private:
};
class ConcreteAbstractionImpB:public AbstractionImp{
public:
ConcreteAbstractionImpB();
~ConcreteAbstractionImpB();
virtual void Operation();
protected:
private:
};
#endif //~_ABSTRACTIONIMP_H_
代码片断 4:AbstractionImp.cpp
//AbstractionImp.cpp
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
AbstractionImp::AbstractionImp(){
}
AbstractionImp::~AbstractionImp(){
}
void AbstractionImp::Operation(){
cout<<"AbstractionImp....imp..."<<endl;
}
ConcreteAbstractionImpA::ConcreteAbstractionImpA(){
}
ConcreteAbstractionImpA::~ConcreteAbstractionImpA(){
}
void ConcreteAbstractionImpA::Operation(){
cout<<"ConcreteAbstractionImpA...."<<endl;
}
ConcreteAbstractionImpB::ConcreteAbstractionImpB(){
}
ConcreteAbstractionImpB::~ConcreteAbstractionImpB(){
}
void ConcreteAbstractionImpB::Operation(){
cout<<"ConcreteAbstractionImpB...."<<endl;
}
代码片断 5:main.cpp
//main.cpp
#include "Abstraction.h"
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[]){
AbstractionImp* imp = new ConcreteAbstractionImpA();
Abstraction* abs = new RefinedAbstraction(imp);
abs->Operation();
return 0;
}
代码说明:桥接模式将抽象和实现分别独立实现,在代码中就是 Abstraction 类和 AbstractionImp类。
关于桥接模式的讨论
桥接是设计模式中比较复杂和难理解的模式之一,也是 OO 开发与设计中经常会用到的模式之一。使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。GoF 在说明桥接模式时,在意图中指出桥接模式"将抽象部分与它的实现部分分离,使得它们可以独立地变化"。这句话很简单,但是也很复杂,连 Bruce Eckel 在他的大作《Thinking in Patterns》中说"桥接模式是 GoF 所讲述得最不好(Poorly-described)的模式",个人觉得也正是如此。原因就在于 GoF 的那句话中的"实现"该怎么去理解:"实现"特别是和"抽象"放在一起的时候我们"默认"的理解是"实现"就是"抽象"的具体子类的实现,但是这里 GoF 所谓的"实现"的含义不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是和继承结合在一起的。而这里的"实现"的含义指的是怎么去实现用户的需求,并且指的是通过组合(委托)的方式实现的,因此这里的实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。理解了这一点也就理解了桥接模式,理解了桥接模式,你的设计就会更加 Elegant 了。实际上上面使用桥接模式和使用带来问题方式的解决方案的根本区别在于是通过继承还是通过组合的方式去实现一个功能需求。因此面向对象分析和设计中有一个原则就是:Favor Composition Over Inheritance。其原因也正在这里。
<上一节
下一节>