定义与特点
桥接(Bridge)模式的定义如下:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
桥接(Bridge)模式的优点是:
由于抽象与实现分离,所以扩展能力强;
其实现细节对客户透明。
缺点是:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。
模式的结构
桥接(Bridge)模式包含以下主要角色。
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
理解桥接模式
可能是由于实际使用场景欠缺,我个人对桥接模式还是比较疑惑的,特别是对于每个角色的定位以及使用场景的理解,导致我对它的使用无从下手。我查阅了不少资料,终于找到了一个我个人认为相较上面角色解释更容易理解的解释:
- 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
- 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
- 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层(包含了一定的业务含义)的操作。
- 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。
抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身。手柄控制杯身,这就是此模式别名“柄体”的来源。
抽象化角色与实现化角色
许多对于桥接模式的应用场景类似都是这样写的:用于解决一个类有多种维度变化的问题。举例都是这样的:
我个人觉得上面的例子特别不好,有两个变化因素不假,但是这两个变化因素是如何来区别,哪个变化因素作为Implementor角色?哪个变化因素作为Abstraction角色呢?为啥不会出现ICoffeeSize和SugerCoffee、MilkCoffee呢?
总结一下:以什么标准去判断哪些变化因素适合放在Abstraction角色上?哪些变化因素适合放在Implementor角色上呢?个人觉得太多的文章都是在浅层面去解释桥接模式,而且这个例子有点牵强,并没有从根本上去教导我们该如何使用思考分析问题,进而培养使用模式解决问题的能力。
更好的例子
上面对于实现化有句话是这样写的:实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。这句话是不是可以这样理解:抽象化是更高维度的变化,而实现化是更低一维度的变化。
在这个例子当中我使用了“大小”和“添加剂”两种Implementor维度,Abstraction我使用了“基本咖啡”和“高级咖啡”的扩展,从层级来讲确实是属于了更好的一个层级。
总结
个人觉得在真实的复杂场景之内,去识别哪些因素属于Abstraction,哪些因素属于Implementor,这个过程才是最具有挑战性的(有点类似于DDD的领域识别),而真正去应用桥接模式反而是一个比较简单的选择问题,我们还是要认识到这点的。