[把你的理性思维慢慢变成条件反射]
本文,我们讲介绍桥接模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:
操作系统:win7 x64
其他软件:eclipse mars,jdk8
-------------------------------------------------------------------------------------------------------------------------------------
经典问题:
驱动程序,机械硬件基础与软件应用紧耦合的产品(如:Window版的应用,IOS版的应用)
思路分析:
要点一:基础平台与软件应用过度耦合导致相同功能重复造轮子。
要点二:在软件实现中, 过度使用继承关系,导致子类对象严重受限于父类。
示例工程:
错误写法:
创建Platfrom.java文件,具体内容如下:
package com.yonyou.iuap.gof_Bridge;
public class Platform {
public void software(){
System.out.println(" software run ...");
}
}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge;
public class Window {
public static void main(String[] args) {
Platform pf = new Platform();
pf.software();
}
}
这里为说明问题,我们将方法主类命名为Platform,各位看官需记得此处的software()方法还需要在多个地方使用。
错误原因:
上文的这种写法,将Platfrom与software紧密的耦合在一起。非常不利于software在其他地方进行使用。(当然可以通过实例化这个类进行调用,等等其他手段)。于是针对上文的写法,常见的做法,将software抽象至父类当中,当有需求时,继承这个父类即可。错误写法(二):
创建PlatformA.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.one;
public class PlatformA extends Software{
@Override
public void softwareA(){
System.out.println("PlatformA softwareA run ...");
}
}
创建PlatformB.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.one;
public class PlatformB extends Software{
@Override
public void softwareA(){
System.out.println("PlatformB softwareA run ...");
}
}
创建Software.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.one;
public class Software {
public void softwareA() {
}
}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.one;
public class Window {
public static void main(String[] args) {
PlatformA pfa = new PlatformA();
pfa.softwareA();
PlatformB pfb = new PlatformB();
pfb.softwareA();
}
}
错误原因:
第二种写法虽然进行了一定程度上的抽象,但是,做一种假设,该software需要在很多类当中进行使用。这将导致继承数量极度增长。最后,所有的platform都受制于software单继承。(Platform数量xsoftware数量=需要维护的类的总数量)
推荐写法:
桥接模式模板代码:
创建Abstraction.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.thire;
public class Abstraction {
protected Implementor implementor;
public void setImplementor(Implementor implementor){
this.implementor = implementor;
}
public void operation(){
implementor.operation();
}
}
创建ConcreteImplementorA.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.thire;
public class ConcreteImplementorA extends Implementor{
@Override
public void operation() {
// TODO Auto-generated method stub
System.out.println("ImplementorA run");
}
}
创建ConcreteImplementorB.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.thire;
public class ConcreteImplementorB extends Implementor{
@Override
public void operation() {
// TODO Auto-generated method stub
System.out.println("ImplementorB run");
}
}
创建Implementor.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.thire;
public abstract class Implementor {
public abstract void operation();
}
创建RefinedAbstraction.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.thire;
public class RefinedAbstraction extends Abstraction{
@Override
public void operation(){
implementor.operation();
}
}
创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Bridge.thire;
public class Window {
public static void main(String[] args) {
Abstraction ab = new RefinedAbstraction();
ab.setImplementor(new ConcreteImplementorA());
ab.operation();
ab.setImplementor(new ConcreteImplementorB());
ab.operation();
}
}
模式总结:
桥接模式结构图:
桥接模式:
将抽象部分与它的实现部分分离,使它们都可以独立的变化。
之所以称为桥接模式,是因为软件中存在两个独立变化的维度,通过该模式可以将这两个维度分离开来,各自维护,使得系统更加符合“单一职责原则”。并且,在两个维度的抽象层上,建立一个关联关系,这个关联关系类似一条连接双方的桥。故称为桥接模式。
组成部分:
Abstraction(抽象类):用于定义抽象类的接口,一般情况下是抽象类,而不是接口。其中,需要定义一个Implementor(实现类接口)类型的对象,并且负责该对象的维护,Abstraction与Implementor存在着关联关系。其中既可以有抽象方法,也可以有具体方法。
RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它是一个具体的类,实现了Abstraction定义的抽象方法,并且RefinedAbstraction也可以调用Implementor中定义的业务方法。
Implementor(实现类接口):定义实现类的接口,该接口不需要与Abstraction保持一致,其内容甚至可以完全不相同。一般情况下,Implementor提供了基本操作,而Abstraction定义复杂操作。Implementor负责声明,ConcreteImplementor负责实现。最后,通过关联关系,Abstraction同事拥有复杂操作与基本操作。由此,由关联关系取代继承关系。
ConcreteImplementor(具体实现类):具体实现Implementor中定义的接口方法,具体实现视需求而定。
模式扩展:
【以下内容来自其他博文,具体请参考末尾】
在软件开发中,适配器模式通常可以与桥接模式联合使用。适配器模式可以解决两个已有接口间不兼容问题,在这种情况下被适配的类往往是一个黑盒子,有时候我们不想也不能改变这个被适配的类,也不能控制其扩展。适配器模式通常用于现有系统与第三方产品功能的集成,采用增加适配器的方式将第三方类集成到系统中。桥接模式则不同,用户可以通过接口继承或类继承的方式来对系统进行扩展。
桥接模式和适配器模式用于设计的不同阶段,桥接模式用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化;而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式。但有时候在设计初期也需要考虑适配器模式,特别是那些涉及到大量第三方应用接口的情况。
下面通过一个实例来说明适配器模式和桥接模式的联合使用:
在某系统的报表处理模块中,需要将报表显示和数据采集分开,系统可以有多种报表显示方式也可以有多种数据采集方式,如可以从文本文件中读取数据,也可以从数据库中读取数据,还可以从Excel文件中获取数据。如果需要从Excel文件中获取数据,则需要调用与Excel相关的API,而这个API是现有系统所不具备的,该API由厂商提供。使用适配器模式和桥接模式设计该模块。
在设计过程中,由于存在报表显示和数据采集两个独立变化的维度,因此可以使用桥接模式进行初步设计;为了使用Excel相关的API来进行数据采集则需要使用适配器模式。系统的完整设计中需要将两个模式联用,如图所示:
反思:
应用场景:
- 系统中可能存在多个维度的变化,需要在抽象类与实现类之间增加灵活性,防止其中发生过多的继承关系。
- 系统的某个运行效果的实现方式在运行时才能确定。抽象类与实现类存在动态变化的可能。
- 对象的不同维度需要独立维护,扩展。
优点:
- 分离抽象接口及实现过程。将传统的继承关系变为桥接关系,从紧耦合变为松耦合。使得抽象和实现可以独立变化与维护。
- 非常好的体现了“单一职责原则”“开闭原则”“合成复用原则”“里氏代换原则”“依赖倒转原则”等等。
缺点:
- 在设计阶段的初始阶段,就需要加入桥接模式的实际思想。一定程度上加大了设计难度。
- 对于维度的识别与分离,需要按照实际需求,严格分析得出,其需要一定的经验积累才能实现。否则,可能弄巧成拙。
-------------------------------------------------------------------------------------------------------------------------------------
至此,被说了很多遍的设计模式---桥接模式 结束
参考资料:
图书:《大话设计模式》
其他博文:http://blog.csdn.NET/lovelion/article/details/7563445