Bridge Pattern

Bridge Pattern,也叫桥接模式,或桥梁模式。

先举一个实例:买水果。店里有很多水果,按新鲜程度看,有刚摘的,也有三天左右的等等,按品种看,有橘子,苹果,香蕉等等。如此,水果可以按不同的分类标准来划分:

  • 按采摘时间:刚摘的 Fresh0, 三天左右的 Fresh3, 十天之外的 Fresh10
  • 按品种:橘子 Orange, 苹果 Apple, 香蕉 Banana

假设要买"最新鲜的香蕉",怎么选?正常的人类一般用下面的方法去找到“最新鲜的香蕉”:

  • 在刚摘的水果中挑选出香蕉
  • 或者在香蕉中挑选出最新鲜的

看起来是再自然不过了,可是怎么用程序语言去表达“最新鲜的香蕉”呢?写一个类 Fresh0Orange extends Fresh0, Orange 么?看起来不错,不过,

  1. Java 语言不支持多继承
  2. 就算 Java 可以多继承。结合水果的新鲜程度和种类,可以有如下组合:“刚摘的橘子,刚摘的苹果,刚摘的香蕉,三天左右的橘子,三天左右的香蕉,……,十天之外的香蕉”。如果还需要别的,也要再新建一个类么?这样的话,类数量会急速增多,会发生所谓的“类爆炸”事件。

一般采用下面的方式:

bridge pattern urml chart

如上图,在 Fruit 中持有对 FruitKind 的引用。全部代码包括 UML 图,可以浏览这个目录:

https://code.google.com/p/cyiridiumsitewikineed/source/browse/trunk/src/designpattern/org/iridium/dp/bridge/,以下是部分能说明问题的代码:

public class Fruit {
    private FruitKind fruitKind;
    public FruitKind getFruitKind() {
        return fruitKind;
    }
    public void setFruitKind(FruitKind fruitKind) {
        this.fruitKind = fruitKind;
    }
   
}
public abstract class FruitKind {
    public abstract void hello();
}
public class Banana extends FruitKind {
    @Override
    public void hello() {
        System.out.println("Banana here");
    }
}
public class FruitFresh0 extends Fruit {
    public FruitFresh0(FruitKind fruitKind) {
        this.setFruitKind(fruitKind);
    }
}
public class Client {
    public static void main(String[] args) {
        //这里选一些"最新鲜的香蕉"
        Fruit fresh0Banana = new FruitFresh0(new Orange());
        fresh0Banana.getFruitKind().hello();
    }
}

这个例子仅仅是为了说明问题,水果的采摘时间和种类,完全可以在水果类中设置两个属性,不过那样的话,就不方便描述桥梁模式了。由上面的代码可以看出桥梁模式可以这么理解,现在已有两组类,求一个新的类,它由两组类中的各一个类“组合”而来,兼有两个类的内容。如果两组类分别由 m 和 n 个,这种组合可以有 m*n 个。通过什么方式去实现这种组合呢?就是把两组类各自的父类组合一下,其中一个父类持有另一个父类的引用,如上面代码中 Fruit 类,持有 FruitKint 的一个引用,在需要用到某具体组合时,再设置一下就好了。

桥梁模式体现了 OO 编程的两个方法:

  1. 找出变化并封装
  2. 优先使用聚合而非继承,这个模式妙哉的地方就是这里!一个类,向两个维度扩展,将一个维度的类变成另外一个维度类的成员,可以将两个维度的类排列组合,避免了的类的爆炸!两个维度可以独立变化。

另外,注意 JDBC-ODBC bridge 是 Adapter 模式,不是 Bridge 模式。