有时候一个类,可能会拥有多个变化维度。比如奶茶可以选择容量大小、口味风味。容易想到的实现方案是通过继承实现各种组合,但是这样会很容易造成类爆炸。那么有没有什么良策呢?答案就是 Bridge Pattern 桥接模式,其是结构性模式的一种,本文就让我们来了解下该模式
模式思想
这里,我们需要构建一个汽车类,我们知道汽车有很多品牌,比如宝马BMW、奔驰Benz。与此同时,汽车的颜色又是五颜六色的,常见的有红、绿、蓝。可以看到在这里对于汽车而言,其存在两个维度——品牌、颜色。前者有2种变化:宝马、奔驰;后者有3种选择:红、绿、蓝。如果使用继承的方式实现各品牌不同颜色的汽车,那将会产生2x3=6个具体的汽车类,示意图如下所示。如果以后需要增加玛莎拉蒂、劳斯莱斯品牌,再新增一个汽车空间大小的维度。可以想见各种组合下的汽车类数目将会激增,即所谓的类爆炸
既然直接通过继承的方式不妥,那么我们就走另外一条路——Bridge Pattern 桥接模式。这里我们先看下使用桥接模式后的示意图,此举是为了给大家建立一个直观的感受。可以看到此时只有2+3=5个具体类。特别是在维度数量、每个维度可选择数目较大时,桥接模式可以大大减少类的数量
可以看到,通过桥接模式我们将两个变化的维度(品牌、颜色)进行了拆分。汽车类通过继承只负责单个维度的变化——即品牌,而另外一个维度的变化(颜色)则是通过组合的方式实现被不同品牌的汽车所使用。现在我们可以看到,两个不同维度是通过各自独立的继承实现体系进行建立。此举一方面很好的体现了类的单一职责原则;另一方面也很好的应用了开闭原则。与此同时,相比较通过静态的继承方式而言,利用动态组合的方式实现了解耦更加灵活。现在就让我们来正式介绍下桥接模式,其通常含有四个角色:
- 抽象化角色:其定义了类的共有方法接口。由于在Java的接口中不可以定义实例变量,故Java中通常是使用抽象类而不是接口。即这里的Car汽车抽象类
- 修正抽象化角色:其通过继承抽象化角色的方式实现了某个维度的变化(一般选择更符合该类本质的维度,比如对这里汽车而言,颜色、品牌这两个维度,显然品牌更符合汽车的本质),通常情况下它们是具体类。比如这里我们通过继承Car类来实现不同品牌的汽车——宝马BMW、奔驰Benz类
- 实现化角色:前面我们说到,汽车有两个维度的变化,我们通过抽象化角色-修正抽象化角色这条继承体系实现了关于品牌维度的变化。那颜色这个维度,怎么办呢?其实很简单,我们再定义一个关于Color颜色的接口即可,其就是实现化角色。这里的实现化可以理解为其是对某个维度变化的实现。这样抽象化角色就可以通过持有一个Color接口的实例以拥有具备该维度变化的能力,这也是该模式名称桥接的由来,即通过组合桥接的方式让Car与Color这两个独立的继承实现体系之间发生联系
- 具体实现化角色:其是实现化角色的具体实现类,比如这里的Red、Green、Blue类
实现
现在让我们来通过Java具体地实现这个例子。首先,我们定义抽象化角色——即Car汽车类,可以看到其内部会持有其它维度接口的实例
1 | /** |
然后,我们通过继承该Car类来实现不同品牌的汽车,即所谓的抽象化角色
1 | /** |
解决了品牌维度后,我们就可以通过实现化角色-具体实现化角色这条继承实现体系来实现颜色这个维度了。这里,我们先通过定义该维度的接口——即实现化角色Color接口
1 | /** |
然后我们通过具体实现化角色来实现具体的颜色
1 | /** |
现在桥接模式就已经被我们完成了,让我们来看看client如果使用它们
1 | /** |
测试结果如下,符合预期
现在,相信大家对桥接模式应该有一个很好的理解了。这里我们只选用了两个维度进行展示,实际上对于多个维度桥接模式也是特别合适的。无非就是通过建立多个实现化角色-具体实现化角色继承实现体系来分别实现各个维度的功能,最后通过桥接实现有机地组合
参考文献
- Head First 设计模式 弗里曼著