image.png

桥接模式 Bridge Design Pattern

Decouple an abstraction from its implementation so that the two can vary independently.——GoF
将抽象和实现解耦,让它们可以独立变化。

关于桥接模式,很多资料中,还有另外一种理解方式:「一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。」通过组合关系来替代继承关系,避免继承层次的指数级爆炸,让 M×NM\times N 个继承类缩减为 M+NM+N 个继承类。关于这里的理解方式,可以看 站内文章面向对象编程 OOP 中「多用组合少用继承」的章节。

理解类的层次结构的两个作用

image.png

类层次结构的作用:

  • 增加新的功能
  • 增加新的实现

增加新的功能

假设类 Something 要增加新功能(增加一个具体方法),会编写 Something 的子类(比如 SomethingGood)。这样就构成了小小的类层次结构:

  • 父类具有基本功能
  • 在子类中增加新的功能

当要继续在 SomethingGood 增加新的功能时,依照这种方式,我们需要继续编写 SomethingGood 的子类 SomethingBetter,这样类的层次结构就加深了。

如果还要继续增加新的功能,我们可以从各个层次的类中找出最符合自己需求的类,然后以它为父类编写子类,并在子类中增加新的功能。这种层次结构被称为「类的功能层次结构」 。

通常来说,类的层次结构关系不应当过深。

增加新的实现

抽象类 AbstractClass 声明抽象方法,定义接口(API),子类 ConcreteClass 负责实现抽象方法,子父类的任务分担,才可以编写具有高可替换性的类。它们之间就构成了一个小小的层次结构:

  • 父类声明抽象方法定义接口(API)
  • 子类通过实现具体方法来实现接口(API)

但是这里的类层次结构不是用于增加功能,而是实现任务分担。这种层次结构称为 「类的实现层次结构」。

如果我们需要其他方式实现 AbstractClass 时,如要实现 NewConcreteClass,那么类的层次结构会发生变化。

类的层次结构的混杂与分离

当我们想要编写子类时,我们需要先思考:我是要增加功能,还是要增加实现?当类的层次结构只有一层时,功能层次结构和实现层次结构是混杂在一个层次结构中的。这样很容易使类的层次结构变得复杂,也难以透彻理解类的层次结构。因为我们难以确定究竟应该在类的哪一个层次结构中去添加子类。

因此,我们需要将「类的功能层次结构」与「类的实现层次结构」分离为两个独立的类层次结构,然后通过 Bridge 模式搭建桥梁。

桥梁模式

image.png

image.png

左边抽象,定义了类的功能层次结构;右边实现,构成类的实现层次结构。

impl 充当桥梁,通过左边的实现类的构造函数传入。左边的类中的方法就是简单的使用 impl 的方法。

Main 中的使用方式:

1
2
3
4
Abstraction a1 = new Abstraction(new ConcreteImplementor1());
Abstraction a2 = new RefinedAbstraction(new ConcreteImplementor2());
a1.method1();
a2.refinedMethodA();

登场角色:

  • Abstraction(抽象部分):该角色位于「类的功能层次结构」的最上层。它使用 Implementor 角色的方法定义了基本的功能。该角色中保存了 Implementor 角色的实例。
  • Implementor(实现部分):该角色位于「类的实现层次结构」的最上层。它定义了用于实现 Abstraction 角色的接口(API)的方法。
  • RefinedAbstraction(精确抽象):在 Abstraction 角色的基础上增加了新功能的角色。
  • Concretelmplementor(具体实现者):该角色负责实现在 Implementor 角色中定义的接口(API)。
  • Client(客户端):仅关心如何与抽象部分合作。但是,客户端需要将抽象对象与一个实现对象连接起来。

image.png

想增加功能时,只需要在「类的功能层次结构」一侧增加类。不必对「类的实现层次结构」做任何修改。

继承是强关联关系,委托是弱关联关系。在设计类时,我们需要充分认识到这一点。

优点 缺点
创建与平台无关的类和程序。 对高内聚的类使用该模式可能会让代码更加复杂。
客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息。
开闭原则。可以新增抽象部分和实现部分,且它们之间不会相互影响。
单一职责原则。抽象部分专注于处理高层逻辑,实现部分处理平台细节。

相关的设计模式:

  • 站内文章模板方法模式:在模板方法模式中使用了「类的实现层次结构」。父类调用抽象方法,而子类实现抽象方法。
  • 站内文章抽象工厂:为了能够根据需求设计出良好的 ConcreteImplementor 角色,有时我们会使用抽象工厂模式。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
  • 站内文章适配器模式:使用桥梁模式可以达到类的功能层次结构与类的实现层次结构分离的目的,并在此基础上使这些层次结构结合起来。而使用适配器模式则可以结合那些功能上相似但是接口(API)不同的类。
  • 站内文章策略模式:如果你需要在运行时切换不同实现方法,可使用桥接模式。注意这里不要和策略模式混淆。设计模式并不仅是一种对类进行组织的方式,它还能用于沟通意图和解决问题。
  • 站内文章建造者模式:主管类负责抽象工作,各种不同的生成器负责实现工作。

本文参考