我学设计模式之桥梁模式
1. 简介
桥梁模式是是对象结构模式。桥梁模式又称柄体模式或接口模式、桥接模式。桥梁模模式用意:将抽象化与实现化脱耦,使得二者可以独立的变化。
熟悉这个模式对于理解面向对象的设计原则,包括“开闭原则(OCP)”以及“组合/聚合原则(CARP)”都很有帮助。
在继续学这个模式之前先来复习了这几个概念:
抽象化:存在于多个实体中的共同概念性的联系,就是抽象化。做为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待。
实现化:抽象化给出的具体实现,就是实现化。
脱耦:就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱。在桥接模式这里是将抽象化和实体化之间的耦合解脱开,或者说将它们之间的强关联改换成弱关联。
强关联:就是在编译时期已经确定的,无法在运行时期改变的关联;
弱关联:就是可以动态确定并且可以在运行时期动态的改变的关联。
显然在java、C#这些面向对象语言中,继承关系是强关联,而聚合关系是弱关联。将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成弱关联。
开闭原则:对扩展开放,对修改关闭。这个原则说的是在设计一个模块的时候,应当使用这个模块可以在不被修改的前提下被扩展。换言之,应当可以在不修改源代码的情况下改变这个模块的行为。
合成聚合复用原则:尽量使用合成/聚合,尽量不要使继承。聚合表示一种弱的“拥有关系”,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类的继承层次会保持较小的规模,并且不太可能增长为不可控制的庞然大物。
2. 桥接模式的结构
下面是桥梁模式的UML图:
这个系统包含两个等级结构:
ü 由抽象化角色和修正抽象化角色组成的抽象化等级结构。
ü 由实现化和两个具体实现化角色所组成的实现化等级结构。
桥梁模式所涉及的角色有:
Ø 抽象化角色:抽象化给出的定义,并保存一个对实现化对象的引用。
Ø 修正抽象化角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
Ø 实现化角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须提出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当给出基于底层操作的更高一层的操作。
Ø 具体实现化角色:这个角色给出的实现化角色接口的具体实现。
3. 桥接模式的用法
用桥接模式来描述手机品牌与手机软件的关系,是用Java来实现:
实现化的源代码:
package com.zsw.bridge;
/** * 手机软件 * @author zsw * 2010年9月26日0:49:29 */ public abstract class HandSetSoft {
public abstract void run(); }
|
具体实现化通讯录:
package com.zsw.bridge;
/** * 通讯录 * @author zsw * 2010年9月26日1:08:36 */ public class HandSetSoftAddressList extends HandSetSoft {
@Override public void run() { System.out.println("------运行手机通讯录!!!"); } }
|
具体实现化游戏:
package com.zsw.bridge; /** * 手机游戏 * @author zsw * 2010年9月26日0:49:29 */ public class HandSetSoftGame extends HandSetSoft{
@Override public void run(){ System.out.println("------运行手机游戏!!!"); } }
|
抽象化手机品牌:
package com.zsw.bridge;
/** * 手机品牌 * @author zsw * 2010年9月26日0:49:29 */ public abstract class HandSetBrand {
protected HandSetSoft soft;
public void setSoft(HandSetSoft soft) { this.soft = soft; }
public abstract void run(); } |
修正抽象化诺基亚手机:
package com.zsw.bridge;
public class HandSetBrandNOKIA extends HandSetBrand {
@Override public void run() { System.out.print("诺基亚手机:"); soft.run(); } }
|
修正抽象化三星手机:
package com.zsw.bridge;
public class HandSetBrandSamsung extends HandSetBrand {
@Override public void run() { System.out.print("三星手机:"); soft.run(); } } |
客户端:
package com.zsw.bridge;
public class Client { public static void main(String[] args) { HandSetBrand hsb = new HandSetBrandNOKIA(); hsb.setSoft(new HandSetSoftGame()); hsb.run();
hsb.setSoft(new HandSetSoftAddressList()); hsb.run();
hsb = new HandSetBrandSamsung(); hsb.setSoft(new HandSetSoftGame()); hsb.run();
hsb.setSoft(new HandSetSoftAddressList()); hsb.run(); } } |
显示结果:
诺基亚手机:------运行手机游戏!!! 诺基亚手机:------运行手机通讯录!!! 三星手机:------运行手机游戏!!! 三星手机:------运行手机通讯录!!! |
4. 桥接模式的应用场景
a) 驱动器软件都可以是桥梁模式的应用。例如:打印机驱动器使用打印机的应用系统与打印机的驱动细节分割开,使得应用系统和打印机驱动器可以相对的独立演化。JDBC驱动器,使用JDBC驱动器的应用程序就是抽象化角色,而驱动器本身扮演实现化角色。
b) 如何一个系统需要在够敬爱呢的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
c) 设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
d) 一个构件多于一个的抽象化角色和实现化角色,系统需要他们之间进行动态耦合。
e) 虽然在系统中使用集成式没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
f) 实现桥梁模式是从底层到高层写。熟悉.net中三层架构的朋友,应该知道,数据访问、业务层、界面层,其实业务层依赖于数据访问层,界面层依赖于业务逻辑层,这也相当于桥接模式。UML图如下:
看到这个例子应该更容易理解这个模式了吧。
g) 诺基亚、三星、索尼都是手机的制造商,手机当中都可以安装一些软件,如通讯录、QQ、游戏等等。现在要设计一个系统来描述这些手机品牌及手机软件类型。设计的UML类图可以如下:
5. 桥接模式的优缺点
优点:
将抽象化与实现化脱耦,使二者可以独立的变化
更好的体现软件的设计原则:开闭原则(OCP)、组合/聚合复用原则(CARP)
6. 参考文献
a) 《Java与模式》
b) 《大话设计模式》
待续……………..
只要深入地理解了设计原则,很多设计模式其实就是原则的应用而已,或许在不知不觉中就在使用设计模式了。