0 引言
0.1 目的
本文档给出设计模式之——Bridge模式的简化诠释,并给出其C++实现。
0.2 说明
Project | Design Pattern Explanation(By K_Eckel) |
Authorization | Free Distributed but Ownership Reserved |
Date | 2005-04-05 (Cherry blossom is Beautiful) |
Test Bed | MS Visual C++ 6.0 |
0.3 参考
在本文档的写作中,参考了以下的资源,在此列出表示感谢:
u 书籍
[GoF 2000]:GoF,Design Patterns-Elements of Reusable Object-Oriented Software
Addison-Wesley 2000/9.
[Martine 2003]:Robert C.Martine, Agile Software Development Principles, Patterns, and Practices, Pearson Education, 2003.
0.4 联系作者
Author | K_Eckel |
State | Candidate for Master’s Degree School of Computer Wuhan University |
E_mail |
2 Bridge模式
2.1 问题
总结面向对象实际上就两句话:一是松耦合(Coupling),二是高内聚(Cohesion)。面向对象系统追求的目标就是尽可能地提高系统模块内部的内聚(Cohesion)、尽可能降低模块间的耦合(Coupling)。然而这也是面向对象设计过程中最为难把握的部分,大家肯定在OO系统的开发过程中遇到这样的问题:
1)客户给了你一个需求,于是使用一个类来实现(A);
2)客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1和A2);
3)客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00和A01,其中A00表示原来的类A)实现不同操作系统上的客户需求,这样我们就有了一共4个类。
4)可能用户的需求又有变化,比如说又有了一种新的算法……..
5)我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。
Bridge模式则正是解决了这类问题。
2.2 模式选择
Bridge模式典型的结构图为:
图2-1:Bridge Pattern结构图
在Bridge模式的结构图中可以看到,系统被分为两个相对独立的部分,左边是抽象部分,右边是实现部分,这两个部分可以互相独立地进行修改:例如上面问题中的客户需求变化,当用户需求需要从Abstraction派生一个具体子类时候,并不需要像上面通过继承方式实现时候需要添加子类A1和A2了。另外当上面问题中由于算法添加也只用改变右边实现(添加一个具体化子类),而右边不用在变化,也不用添加子类,一切都变得Elegant!
2.3 实现
2.3.1 完整代码示例(code)
Bridge模式的实现起来并不是特别困难,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用C++实现,并在VC 6.0下测试运行)。
代码片断1:Abstraction.h #ifndef _ABSTRACTION_H_ class AbstractionImp; class Abstraction virtual void Operation() = 0; protected: private: }; class RefinedAbstraction:public Abstraction ~RefinedAbstraction(); void Operation(); protected: private: }; #endif //~_ABSTRACTION_H_ |
代码片断2:Abstraction.cpp #include "Abstraction.h" #include <iostream> Abstraction::Abstraction() } Abstraction::~Abstraction() } RefinedAbstraction::RefinedAbstraction(AbstractionImp* imp) RefinedAbstraction::~RefinedAbstraction() } void RefinedAbstraction::Operation() |
代码片断3:AbstractionImp.h #ifndef _ABSTRACTIONIMP_H_ class AbstractionImp virtual void Operation() = 0; protected: private: }; class ConcreteAbstractionImpA:public AbstractionImp ~ConcreteAbstractionImpA(); virtual void Operation(); protected: }; class ConcreteAbstractionImpB:public AbstractionImp ~ConcreteAbstractionImpB(); virtual void Operation(); protected: }; #endif //~_ABSTRACTIONIMP_H_ |
代码片断4:AbstractionImp.cpp #include "AbstractionImp.h" #include <iostream> AbstractionImp::AbstractionImp() } AbstractionImp::~AbstractionImp() } void AbstractionImp::Operation() ConcreteAbstractionImpA::ConcreteAbstractionImpA() } ConcreteAbstractionImpA::~ConcreteAbstractionImpA() } void ConcreteAbstractionImpA::Operation() ConcreteAbstractionImpB::ConcreteAbstractionImpB() } ConcreteAbstractionImpB::~ConcreteAbstractionImpB() } void ConcreteAbstractionImpB::Operation() |
代码片断5:main.cpp #include "Abstraction.h" #include <iostream> int main(int argc,char* argv[]) Abstraction* abs = new RefinedAbstraction(imp); abs->Operation(); |
2.3.2 代码说明
Bridge模式将抽象和实现分别独立实现,在代码中就是Abstraction类和AbstractionImp
类。
2.4 讨论
Bridge是设计模式中比较复杂和难理解的模式之一,也是OO开发与设计中经常会用到的模式之一。使用组合(委托)的方式将抽象和实现彻底地解耦,这样的好处是抽象和实现可以分别独立地变化,系统的耦合性也得到了很好的降低。
GoF在说明Bridge模式时,在意图中指出Bridge模式“将抽象部分与它的实现部分分离,使得它们可以独立地变化”。这句话很简单,但是也很复杂,连Bruce Eckel在他的大作《Thinking in Patterns》中说“Bridge模式是GoF所讲述得最不好(Poorly-described)的模式”,个人觉得也正是如此。原因就在于GoF的那句话中的“实现”该怎么去理解:“实现”特别是和“抽象”放在一起的时候我们“默认”的理解是“实现”就是“抽象”的具体子类的实现,但是这里GoF所谓的“实现”的含义不是指抽象基类的具体子类对抽象基类中虚函数(接口)的实现,是和继承结合在一起的。而这里的“实现”的含义指的是怎么去实现用户的需求,并且指的是通过组合(委托)的方式实现的,因此这里的实现不是指的继承基类、实现基类接口,而是指的是通过对象组合实现用户的需求。理解了这一点也就理解了Bridge模式,理解了Bridge模式,你的设计就会更加Elegant了。
实际上上面使用Bridge模式和使用带来问题方式的解决方案的根本区别在于是通过继承还是通过组合的方式去实现一个功能需求。因此面向对象分析和设计中有一个原则就是:Favor Composition Over Inheritance。其原因也正在这里。