设计模式——桥接模式

来源:互联网 发布:图解汉诺塔递归算法c 编辑:程序博客网 时间:2024/05/22 02:21

1. 简介

  当一个抽象可能有多个实现时,通常用继承协调它们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立的进行修改、扩充和重用。这种依赖关系限制了灵活并最终限制了复用性。
  在面向对象设计中,我们有一个很重要的原则,那就是合成/聚合复用原则。即优先使用对象合成/聚合,而不是类继承。这也就引出这次要提到的桥接模式。
  桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。这里的抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象。可能听起来很难理解,先往后看再来理解这句话。

2. 结构图

  下面是桥接模式的结构图:

  • Abstraction:定义抽象类的接口。维护一个指向Implementor类型的指针。
  • RefinedAbstraction:扩充由Abstraction定义的接口。
  • Implementor:定义实现类的接口,该接口不一定要与Absstraction的接口完全一致;事实上这两个接口可以完全不同。一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些操作的较高层次操作。
  • ConcreteImplementor:实现Implementor接口并定义它的具体实现。

3. 案例分析

  让我们考虑在一个用户界面工具箱中,一个可移植的窗口(Window)抽象部分的实现。例如,这一抽象部分应该允许用户开发一些在Win系统(微软)和Mac系统(苹果)中都可以使用的应用程序。运用继承机制,我们可以定义Window抽象类和它的两个子类WinWindow和MacWindow,由它们分别实现不同系统平台上的Window界面。但是继承机制有两个不足之处:
  1. 扩展Window抽象使之适用于不同种类的窗口或新系统平台很不方便。假设有Window的一个子类IconWindow,它专门将Window抽象用于图标处理。为了使IconWindow支持两个系统平台,我们必须实现两个新类WinIconWindow和MacIconWindow,更为糟糕的是,我们不得不为每一种类型的窗口定义两个类。而为了支持第三个系统平台我们还必须为每一种窗口定义一个新的Window子类。
  2. 继承机制使得客户代码与平台相关。每当客户创建一个窗口时,必须要实例化一个具体的类,这个类有特定的实现部分。例如,创建WinWindow对象会将Window抽象和Win Window的实现绑定起来,这使得客户程序依赖于Win Window的实现部分。这将使得很难将客户代码移植到其他平台上去。
  客户在创建窗口时应该不涉及到其具体实现部分。仅仅是窗口的实现部分依赖于应用运行平台。这样客户代码在创建窗口时就不涉及到特定的平台。
Bridge模式解决以上问题的方法是,将Window抽象和它的实现部分分别放在独立的类层次结构中。其中一个类层次结构针对窗口接口(Window、IconWindow、TransientWindow),另外一个独立的类层次结构针对平台相关的窗口实现部分,这个类层次结构的根类为WindowImp。例如WinWindowImp子类提供了一个基于Win Window系统的实现。
  对Window子类的所有操作都是用WindowImp接口中的抽象操作实现的。这就将窗口的抽象与系统平台相关的实现部分分离开来。因此,我们将Window和WindowImp直接的关系称之为桥接,因为它的抽象类与它的实现之间起到了桥梁作用,使它们可以独立地变化。
/**********************************************************************************Copyright(C),Your Company*FileName:  Window.h*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  相当于结构图Abstraction角色——可移植的窗口抽象部分实现*Others:**********************************************************************************/#ifndef _WINDOW_H#define _WINDOW_Hclass CWindowImp;class CWindow{public:CWindow() { }virtual ~CWindow() { }virtual void DisplayWindow(void) = 0;void SetWindowImp(CWindowImp *pWindowImp){this->m_pWindowImp = pWindowImp;}protected:CWindowImp *GetWindowImp(void){return this->m_pWindowImp;}private:CWindowImp *m_pWindowImp;};#endif
/**********************************************************************************Copyright(C),Your Company*FileName:  TransientWindow.h*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  相当于结构图RefinedAbstraction角色——临时窗口的抽象*Others:**********************************************************************************/#ifndef ICON_WINDOW_H#define ICON_WINDOW_H#include <cstddef>#include <iostream>#include "Window.h"#include "WindowImp.h"class CTransientWindow : public CWindow{public:CTransientWindow() { }~CTransientWindow() { }void DisplayWindow(void){CWindowImp *pWindowImp = NULL;pWindowImp = GetWindowImp();if (pWindowImp != NULL){pWindowImp->Draw();std::cout << "显示一个临时窗口" << std::endl;}}};#endif//#ifndef ICON_WINDOW_H
/**********************************************************************************Copyright(C),Your Company*FileName:  IconWindow.h*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  相当于结构图RefinedAbstraction角色——图标窗口的抽象*Others:**********************************************************************************/#ifndef ICON_WINDOW_H#define ICON_WINDOW_H#include <cstddef>#include <iostream>#include "Window.h"class CIconWindow:public CWindow{public:CIconWindow() { }~CIconWindow() { }void DisplayWindow(void){CWindowImp *pWindowImp = NULL;pWindowImp = GetWindowImp();if (pWindowImp != NULL){pWindowImp->Draw();std::cout << "显示一个图标窗口" << std::endl;}}};#endif//#ifndef ICON_WINDOW_H
/**********************************************************************************Copyright(C),Your Company*FileName:  WindowImp.h*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  相当于结构图Implementor角色——针对平台相关的抽象实现部分*Others:**********************************************************************************/#ifndef _WINDOW_IMP_H#define _WINDOW_IMP_Hclass CWindowImp{public:CWindowImp() { }virtual ~CWindowImp() { }virtual void Draw(void) = 0;};#endif//#ifndef _WINDOW_IMP_H
/**********************************************************************************Copyright(C),Your Company*FileName:  WinWindowImp.h*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  相当于结构图ConcreteImplementor角色——针对微软平台相关实现部分*Others:**********************************************************************************/#ifndef _WIN_WINDOW_IMP_H#define _WIN_WINDOW_IMP_H#include <iostream>#include "WindowImp.h"class CWinWindowImp:public CWindowImp{public:CWinWindowImp() { }~CWinWindowImp() { }void Draw(void){std::cout << "微软操作系统窗口实现:";}};#endif//#ifndef _WIN_WINDOW_IMP_H
/**********************************************************************************Copyright(C),Your Company*FileName:  MacWindowImp.h*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  相当于结构图ConcreteImplementor角色——针对苹果平台相关实现部分*Others:**********************************************************************************/#ifndef _MAC_WINDOW_IMP_H#define _MAC_WINDOW_IMP_H#include <iostream>#include "WindowImp.h"class CMacWindowImp : public CWindowImp{public:CMacWindowImp() { }~CMacWindowImp() { }void Draw(void){std::cout << "苹果操作系统窗口实现:";}};#endif//#ifndef _MAC_WINDOW_IMP_H
/**********************************************************************************Copyright(C),Your Company*FileName:  main.cpp*Author:  Huangjh*Version:*Date:  2017-11-09*Description:  桥接模式的测试用例*Others:**********************************************************************************/#include "Window.h"#include "MacWindowImp.h"#include "WinWindowImp.h"#include "TransientWindow.h"#include "IconWindow.h"int main(void){CWindow *pWindow = new CTransientWindow();CWindowImp *pWindowImp = new CWinWindowImp();pWindow->SetWindowImp(pWindowImp);pWindow->DisplayWindow();return 0;}

4. 优缺点

4.1 优点

  • 分离接口及其实现部分 一个实现未必不定的绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。将Abstraction与ImpLementor 分离有助于降低对实现部分编译时刻的 依赖,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要 有这个性质。
  • 提供可扩充性 你可以独立地对Abstraction与ImpLementor层次结构进行扩充。
  • 实现细节对客户透明 你可以对客户隐藏实现细节,例如共享Implementor对象 以及相应的引用计数机制 (如果有的话)。

5. 适用性

  以下一些情况使用Bridge模式:
  • 你 不希望在抽象和实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应该可以被选择或者切换。
  • 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这是Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
  • 对一个抽象的实现部分的修改对客户不产生影响,即客户的代码不必重新编译。
  • 你想对客户完全隐藏抽象的实现部分。在C++中。类的表示在类接口中是可见的。
  • 正如之前讨论的,有许多类要生成。这样一种;类层次结构说明你必须将一个对象分解成两部分。
  • 你想在对个对象间共享实现(可能使用引用计数),但同时要求客户不知道这一点。

6. 参考资料

  • 《大话设计模式》
  • 《设计模式——可复用的面向对象软件的基础》

原创粉丝点击