4.2-Bridge「Handle」-桥接-结构型模式

又称 Handle/Body

0.1.1 用途

将抽象部分与它的实现部分分离,使它们可以独立地变化。 继承 VS Bridge:

0.1.2 示例

场景:一个 GUI 工具箱中的可移植窗口需要支持 XWindows 和 PM 系统 实现方法:

  • 继承:定义 Window 抽象类和它的两个子类 XWindow 与 PMWindow,由它们分别实现不同系统平台上的 Window 界面。
    • 缺陷:
      • 扩展 Window 抽象使之适用于不同种类的窗口或新的系统平台很不方便(需要为 Windows 的每个子类针对每个平台分别创建一个类)
      • 继承机制使得客户代码与平台相关。
  • Bridge 模式:将接口抽象和实现放在彼此独立的类层次结构中:
    • 对 Window 子类的所有操作都是用 WindowImp 接口中的抽象操作实现的
    • 将 Window 与 WindowImp 之间的关系称为桥接,它在抽象类与它的实现之间起到了桥梁作用,使它们可以独立地变化。

0.1.3 适用性

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

0.1.4 结构

  • Abstraction:抽象接口(例如示例中的 Window),维护一个指向实现(Implementor)的指针
  • RefinedAbstraction:精化的接口(例如示例中的 IconWindow)
  • Implementor:实现类的接口(示例中的 WindowImp)
    • 该接口无需和 Abstraction 的接口一致。一般来讲:
      • Implementor 接口仅提供基本操作
      • Abstraction 则定义了基于这些基本操作的较高层次的操作
  • ConcreteImplementor:实现 Implementor 的具体接口(示例中的 XWindowImp、PMWindowImp)

Abstraction 将 client 的请求转发给它的 Implementor 对象

0.1.5 优缺点

  • 分离接口及其实现部分,抽象类的实现可以在运行时进行配置、变更
    • (用于确保一个类库不同版本之间的兼容性)有助于降低对实现部分编译时的依赖性,当改变一个实现类时,并不需要重新编译 Abstraction 类和它的客户程序。
    • 接口与实现分离有助于分层,从而产生更好的结构化系统
  • 提高可扩充性你可以独立地对 Abstraction 和 Implementor 层次结构进行扩充
  • 实现细节对客户透明 (可以对客户隐藏实现细节)
    • 例如在 C++中,Implementor 类的类接口可以在一个私有的头文件中定义,这个文件不提供给客户。这样你就对客户彻底隐藏了一个类的实现部分。

0.1.6 实现

  • 在仅有一个实现的时候,没有必要创建一个抽象的 Implementor 类
  • 创建合适的 Implementor 类有多种方法:
    • 指定特定的 ConcreteImplementor 类:如果 Abstraction 知道所有的 ConcreteImplementor 类,它就可以在它的构造器中对其中的一个类进行实例化
    • 指定缺省实现,并根据需要切换实现:例如将容器起初初始化为数组,容量过大后切换为 hash 表
    • 由代理对象决定:例如 AbstractFactory 模式中的选择窗口样式的工厂类
  • C++中无法使用多重继承实现真正的 Bridge 模式(会把接口和特定实现绑定):public 继承接口,private 继承实现
  • 共享 Implementor 对象:CPP 中常用 Handle/Body 的方法在对象之间共享实现(通过重载赋值运算符执行引用计数)
1
2
3
4
5
6
7
8
Handle& Handle::operator=(const Handle& other) {
  other._body->Ref();
  _body->Unref();
  if (_body->RefCount() == 0) {
    delete _body;
  }
  _body = other._body;
}