一、概念准备
在理解桥接模式之前,先要理解面向对象程序设计中的两个概念:
- 类的功能层次结构:假设现在有一个类Something,这个类有一些成员属性和成员方法,但是现有的功能不能满足要求,因此我们想扩展这个类,给这个类创建一个子类SomethingBetter来继承它,并在子类中添加更多属性和方法。像这样不同的功能位于不同的类继承结构中,就构成了类的功能层次结构。
- 类的实现层次结构:回顾一下抽象类的作用,抽象类声明抽象方法(定义了方法接口API),然后在继承它的子类中实现相关方法。由于我们将方法定义和实现分离到了不同的类中,从而实现了类的高可替换性,并有了模板Template Method方法模式。虽然这里也有父类和子类的继承关系,但是这里的继承并不是为了增加新功能,而是为了完成“定义——实现 分离”中的实现环节。这种层次结构就是类的实现层次接口。
二、模式说明
如果类的设计都只有一层(程序中自定义的类不存在继承关系),这时候类的功能层次结构和实现层次结构是混杂在一起的,也很难扩展(无法确定应该继承哪个类来扩展功能,同一个方法如果想替换其实现也无法做到)。因此需要按照上面说的,将类的功能层次和实现层次分开,为了在分开后能继续保持一定的联系,这时候就需要一个桥梁,将它们连接起来。(Bridge也有桥梁的意思,连接两个事物)
三、代码示例
在下面的桥接模式代码示例中,着重关注哪些时功能层次,哪些是实现层次,二者又是如何桥接起来的。
1、类的功能层次结构
Display类:Display类的功能是抽象的,负责“显示”一些东西,它位于功能层次结构的最上层。open、print、close这三个方法是Display类提供的接口,它们定义了显示的步骤,这三个方法调用的是Display实现类的对象。
package com.designpattern.cn.bridgepattern; public class Display { private DisplayImpl impl; public Display(DisplayImpl impl){ this.impl = impl; } public void open(){ impl.rawOpen(); } public void print(){ impl.rawPrint(); } public void close(){ impl.rawClose(); } public final void display(){ open(); print(); close(); } }
CountDisplay类:CountDisplay类在Display类的基础上增加了一个新功能,具有“只显示规定次数”的功能。因此,这属于“类的功能层次范畴”。
package com.designpattern.cn.bridgepattern; public class CountDisplay extends Display { public CountDisplay(DisplayImpl impl){ super(impl); } public void multiDisplay(int times){ open(); for(int i=0; i < times; i++){ print(); } close(); } }
2、类的实现层次结构
DisplayImpl类:位于“类的实现层次结构”的最上层。DisplayImpl是抽象类,声明了rawOpen、rawPrint、rawClose三个抽象方法。
package com.designpattern.cn.bridgepattern; public abstract class DisplayImpl { public abstract void rawOpen(); public abstract void rawPrint(); public abstract void rawClose(); }
StringDisplayImpl类:它是仙子字符串的类,但它并不是直接显示字符串,而是继承了DisplayImpl类,作为子类来使用rawOpen、rawPrint、rawClose三个方法显示字符串。
package com.designpattern.cn.bridgepattern; public class StringDisplayImpl extends DisplayImpl { private String string; private int width; public StringDisplayImpl(String string){ this.string = string; this.width = string.getBytes().length; } @Override public void rawOpen(){ printLine(); } @Override public void rawPrint(){ System.out.println("|" + string + "|"); } @Override public void rawClose() { printLine(); } private void printLine(){ System.out.print("+"); for(int i = 0; i < width; i++){ System.out.println("-"); } System.out.println("+"); } }
3、Main类测试结果
四、模式类图
五、模式中的角色
- Abstraction抽象化:类的功能层次的最上层,它使用Implementor角色的方法定义了基本功能,如上面的Display类。
- RefinedAbstraction改善后的抽象化:增加了Abstraction的功能,如上面的CountDisplay类。
- Implementor实现者:类的实现层次结构最上层,定义了用于实现Abstraction的角色的接口方法,如上面的DisplayImpl类。
- ConcreteImplementor具体实现者:负责实现Implementor角色定义的接口方法,如上面的StringDisplayImpl类。
那么,这其中的桥梁Bridge是什么呢?对于示例程序,Display类和DisplayImpl是如何关联的,从程序中看出,这里的桥梁应该是impl字段。
六、相关的设计模式
- 模板方法Template Method模式:模板方法模式就是使用了桥接模式中的类的实现层次结构。父类调用抽象方法,子类实现具体方法。
- 抽象工厂Abstract Factory模式:可以使用抽象工厂设计出更好的ConcreteImplementor类。
- 适配器Adapter模式:桥接模式连接了类的结构层次和实现层次,适配器模式则将功能类似,但是接口不匹配的类结合在一起。
七、扩展
- 采用bridge桥接模式分离功能层次和实现层次,更有利于类的扩展,如果要增加功能,只需在功能层次这一侧做修改,无需调整实现层次,并且所有的实现层次都能利用增加的功能。
- 继承是强关联,委托是弱关联。继承可以方便的得到功能更强的类,但是一旦使用继承,则在类之间形成了强关联关系,如果想改变或者替换其中的一个类,就必须修改类的代码。上面的例子中,使用了委托,只有当Display类的实例生成时,才作为参数,与被传入的类构成关联关系(示例程序中,只有Main类生成Display类和CountDisplay类的实例时,才将StringDisplayImpl类的实例作为参数传递给Display类和CountDisplay类)。除了StringDisplayImpl类,我们可以使用任意一个ConcreteImplementor角色传递给Display类和CountDisplay类,就能很容易的改变实现(只需修改Main类,Display类和DisplayImpl无需任何修改)。