设计模式之桥接模式

在正式介绍桥接模式之前,我先跟大家谈谈两种常见文具的区别,它们是毛笔和蜡笔。假如我们需要大中小 3 种型号的画笔,能够绘制 12 种不同的颜色,如果使用蜡笔,需要准备 3×12 = 36 支,但如果使用毛笔的话,只需要提供 3 种型号的毛笔,外加 12 个颜料盒即可,涉及到的对象个数仅为 3 + 12 = 15,远小于36,却能实现与 36 支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有 12 种颜色,对应的蜡笔需增加 12 支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和 型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响。如果使用软件工程中的术语,我们可以认为在蜡笔中颜色和型号之间存在较强的耦合性,而毛笔很好地将二者解耦,使用起来非常灵活,扩展也更为方便 。在软件开发中,我们也提供了一种设计模式来处理与画笔类似的具有多变化维度的情况,即本章将要介绍的桥接模式。

桥接模式概述

桥接模式将抽象部分与实现部分分离。如果软件系统中存在两个独立变化的维度,通过桥接模式可以将两个维度分离出来,使两者可以独立扩展。

桥接模式 UML 类图

bridge_uml.gif

根据上图可以看到桥接模式包括以下角色:

  • Abstraction :抽象部分,该类保持一个对实现部分的引用,抽象部分中的方法需要调用实现部分的对象来实现。
  • RefinedAbstraction : 抽象部分的具体实现。
  • Implementor : 实现部分,定义实现类的接口,这个接口不一定要与 Abstraction 的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor 接口仅提供基本操作,而 Abstraction 定义的接口可能会做更多更复杂的操作。Implementor 接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在 Abstraction 中不仅拥有自己的方法,还可以调用到 Implementor 中定义的方法,使用关联关系来替代继承关系。
  • ConcreteImplementor : 实现部分的具体实现,完善实现部分中方法定义的具体逻辑。

桥接模式具体实现

在使用桥接模式时,我们首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。通常情况下我们将独立变化的两个维度中与类的关系最为密切的维度设计为抽象部分,而将另一个维度设计为实现部分。例如我们将文章开头提到的毛笔例子中的毛笔的型号维度作为抽象部分,而将颜色维度作为实现部分,具体的设计图入下:
mao_bi.gif
下面我们根据上图进行代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//毛笔接口
abstract class Brush {
protected Color mColor;

public void setColor(Color color){
this.mColor=color;
}

public abstract void draw();
}

class LargeBrush{
public void draw(){
//用大号笔 mColor.getColor() 色进行绘图
}
}

class MiddleBrush{
public void draw(){
//用中号笔 mColor.getColor() 色进行绘图
}
}

class SmallBrush{
public void draw(){
//用小号笔 mColor.getColor() 色进行绘图
}
}

interface Color {
String getColor();
}

class RedColor(){
String getColor(){
return "red";
}
}

class BlueColor(){
String getColor(){
return "blue";
}
}

class GreenColor(){
String getColor(){
return "green";
}
}

可以看到通过使用桥接模式,其中一个维度的变化不会影响到另一个维度,现在无论是要增加毛笔的型号,还是要增加颜色都很容易,在桥接模式中体现了很多面向对象设计原则的思想,包括“单一职责原则”、“开闭原则”、“合成复用原则”、“里氏代换原则”、“依赖倒转原则”等。

Android 源码中的桥接模式

桥接模式在 Android 源码中应用广泛,比较典型的有 Adapter 与 AdapterView 的桥接,Window 与 WindowManager 的桥接模式。

Adapter 与 AdapterView 的桥接
abs_bridge.jpg
Window 与 WindowManager 的桥接
win_bridge.jpg

通过以上两 UML 类图应该能明显的看出来桥接模式的影子。

总结

桥接模式的好处非常明显,桥接模式将两个维度上的变化进行分离,极大的提高了程序的扩展性。但是要用好桥接模式却不容易,对桥接模式来说,理解很简单,但是设计却不容易。