桥接模式

对象结构型模式

模式动机

假设现在需要绘制矩形、圆形、椭圆、正方形等图形,那么至少需要4个形状类;如果此时又需要给各个图形上色,比如白灰黑,那么有两种方案可供选择:

  1. 每个图形都提供一个对应颜色的版本(使用继承)

  2. 按需组合图形和颜色

显然,方案二的耦合更小,类数量更少,更容易扩展,这就是桥接模式

应用

桥接模式将继承关系转换为关联关系,降低了类与类之间的耦合;同时它也将抽象部分和实现部分分离,使它们都可以独立变化

桥接模式包含如下角色:

  • 抽象类Abstraction
  • 扩充抽象类RefinedAbstraction
  • 实现类接口Implementor
  • 具体实现类ConcreteImplementor

分析

是不是感觉和前面某些设计模式有些眼熟,这里需要理解一下桥接模式的设计思想,重点需要理解如何将抽象化与实现化解耦,使二者独立变化:

抽象化

指忽略一些信息,把不同实体当同样实体对待,抽取共同性质形成类的过程就是抽象化

实现化

针对抽象化给出具体实现,就是实现化,两者是互逆的,实现化包含了一些具体的性质,这些性质在抽象化的过程中被忽略了

解耦

解耦就是把抽象化和实现化之间的耦合释放,或者说把它们之间的强耦合转为弱耦合,比如继承变成关联,桥接模式就是通过使用组合或聚合关系而不是继承关系来解耦抽象化和实现化(一般的实现和抽象都是继承关系),使两者能够独立变化。

典型实现类接口代码如下:

public interface Implementor {
public void operationImpl();
}

典型抽象类代码如下:

public abstract class Abstraction {
protected Implementor impl;
public void setImpl(Implementor impl) {
this.impl=impl;
}
public abstract void operation();
}

典型扩充抽象类代码如下:

public class RefinedAbstraction extends Abstraction {
public void operation() {
// ...
impl.operationImpl();
// ...
}
}

实例:模拟毛笔

毛笔可以通过蘸不同的颜料画出不同的颜色,不同类型的毛笔甚至可以通过换头来实现(当然本例不用考虑这种情形)。现在提供了三种型号的画笔,要求绘制5种颜色,如果用蜡笔,就需要15个蜡笔类(3×53\times5,蜡笔的颜色是定死的);而如果用毛笔只需要3+5=83+5=8个类(三种毛笔,五个颜料盒),可以利用桥接模式来模拟毛笔的实现。

实例:跨平台视频播放器

现在果需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Linux、Unix等)上播放多种格式的视频文件,常见的视频格式包括MPEG、RMVB、AVI、WMV等。

优点

  1. 分离抽象接口及其实现
  2. 桥接模式看起来类似多继承,但是多继承违背了单一职责原则(一个类继承多个类就可以产生多种变化,对应多种职责),复用性不好,而桥接模式并不违反,比多继承更好
  3. 提高了系统可扩展性,两个不同维度中扩展任意一个维度都不需要修改原有代码,符合开闭原则
  4. 实现对于客户来讲是透明的,隐藏了具体实现细节

缺点

  1. 增加系统的理解难度和设计难度,关联关系建立在抽象层,需要开发者做出很好的抽象化
  2. 要能正确识别出独立变化的维度(这也是1中难做抽象化的原因),因此也有局限性(没有独立变化的维度就用不了了)

适用场合

  • 系统需要在构件的抽象化角色和具体化角色之间增加灵活性,避免两个层次之间建立静态继承关系,使用桥接模式可以在抽象层为他们建立关联
  • 抽象化角色和实现化角色可以以继承方式各自独立扩展
  • 一个类存在两个独立变化的维度,且两个维度都需要扩展
  • 不希望因为大量使用继承导致类的个数急剧增加的系统

Java跨平台(不同系统的JVM不同,但是Java程序运行在JVM里),用桥接模式实现

模式扩展

适配器模式 + 桥接模式

两个模式用于设计的不同阶段,一般来说桥接模式用于初步设计,对于存在两个独立变化维度的类,将其分为抽象化和实现化;在设计后期,当发现系统已经无法和现有类协作时(接口不统一),采用适配器模式

不过一些特殊情况下设计初期也会考虑适配器模式,比如希望大量复用第三方应用接口的情况。

(•‿•)