桥模式
场景
现假设市面上有多种不同型号的电视机,和一些不同的遥控器生产产商。遥控器生产产商要为这些电视机进行生产遥控器,可是这些电视机型号太多,有一些电视机的型号太杂,甚至在后期会出现一些不同型号的电视机。
普通聚合方案
在这个一般方案中,遥控器产商要为每一种电视机生产一种遥控器。类图如下
每个具体的遥控器都去继承或是实现遥控器的抽象或是接口,并且在每个具体的遥控器内部都聚合了各自的电视机接口。这种方案可以解决问题,可以解决遥控器与电视机的对应关系。可是,却有一个问题,就像上面场景中所说的,如果这里的有一些新的电视机出现,那么生产遥控器的产商还得去生产新的遥控器。这样一来各自的遥控器数量不但不好把握,而且这样的设计很繁杂。
桥模式
上面一般的聚合方案无法解决遥控器的一般性,对于新的机型无法兼容和适应。对于这样在抽象或是实现上会有多维度扩展的情况,我们就必须重新设计代码结构了。下面是桥模式,以及桥模式是如何解决这个问题的。
定义
将抽象部分()与实现部分()分离,使它们可以独立地变化。
分析
桥模式的应用场景是某一个抽象或是实现可能存在多维度的变化。就像上面的遥控器实例,我们在遥控器这个抽象上,可能会有不同的生产产商;在电视机这个实现上可能会RCA、Sony等等。桥模式可以让遥控器的抽象和电视机的实现在各自的方向上多维度变化,而不用关心其他的事情。
在上面我也分析了一般的聚合方案是不可靠的,它的确可以解决一些问题,可是却是繁琐的,也是不必要的。
这里再说一个大家可能都比较熟悉的例子。比如我们现在编写的Java程序,与运行平台之间的关系。我们可以有很多很多的Java程序,而运行平台也可以是、Linux或是Mac。我们的Java程序需要在所有的平台上能够执行,每一个平台也要保证能够全部的Java程序。
类图
这里我们让遥控器的抽象类聚合电视机的接口。因为所以的电视都实现电视机这个接口,那么遥控器的抽象类也就聚合了所有的电视机实现。这样不管你电视机的型号如何改变,遥控器都可以不做修改也能继续工作。
注:当时我在这里存在一个小疑问,为什么我们的电视机接口不是聚合到遥控器的子类中,而是聚合到遥控器的抽象类中呢?也就是说,这里我有没有必要有一个遥控器的抽象类?
现在我假设这里不存在这个的抽象类,那么将持有TV这个接口,可是这样在遥控器这个维度上都不好扩展了。桥模式的牛X之处就在于它可以让多维度的抽象和实现都可以在多维度上进行任意扩展。
逻辑实现
基于上面的类图,可编写Java代码。
TV.java
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
RCA.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Sony.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
.java
- 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
- 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
模式评价 优点 缺点
电视机的实现被解耦,它和遥控器之间不再永久绑定
相对比较复杂
遥控器和电视机可以独立扩展,且不会影响对方
Ref 源码链接