一、概述
将抽象部分与它的具体实现部分分离。使它们都可以独立地变化。通过组合的方式建立两个类之间联系,而不是继承。
Bridge 模式又叫做桥接模式,是构造型的设计模式之一。Bridge模式基于类的最小设计原则,通过使用封装,聚合以及继承等行为来让不同的类承担不同的责任。
它的主要特点是把抽象(abstraction)与行为实现(implementation)分离开来,从而可以保持各部分的独立性以及应对它们的功能扩展。
常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
1.1、适用场景
如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
一个类存在两个(或多个)独立变化的维度。且这两个(或多个)维度都需要独立进行扩展
虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
不希望使用继承,或因为多层继承导致系统类的个数剧增
1.2、优缺点
优点:
- 分离抽象部分及其具体实现部分
- 提高了系统的可扩展性
- 符合开闭原则,可动态的切换实现
- 符合合成复用原则,实现细节对客户端透明,可以对用户隐藏实现细节
缺点:
- 增加了系统的理解与设计难度
- 需要正确地识别出系统中两个独立变化的维度
对比其他模式
桥接模式和组合模式
- 组合模式强调的是部分和整体间的组合,桥接模式强调的是平行级别上,不同类的组合
桥接模式和适配器模式
- 适配器模式改变已有的接口,桥接模式分离抽象和具体
1.3、类图角色及其职责
桥接模式的角色和职责
1、Client:Bridge模式的使用者
2、Abstraction(Car)
(1)抽象类接口(接口或抽象类)
(2)维护对行为实现(Implementor)的引用
3、Refined Abstraction(Bus、Suv):Abstraction子类
4、Implementor(Engine): 行为实现类接口 (Abstraction接口定义了基于Implementor接口的更高层次的操作)
5、ConcreteImplementor(OilEngine、ElectricityEngine):Implementor子类
抽象画
接口定义
public interface Sourceable { public void method(); }
分别定义两个实现类:
public class SourceSub1 implements Sourceable { @Override public void method() { System.out.println("this is the first sub!"); } }
public class SourceSub2 implements Sourceable { @Override public void method() { System.out.println("this is the second sub!"); } }
定义一个桥,持有Sourceable的一个实例:
public abstract class Bridge { private Sourceable source; public void method(){ source.method(); } public Sourceable getSource() { return source; } public void setSource(Sourceable source) { this.source = source; } }
public class MyBridge extends Bridge { public void method(){ getSource().method(); } }
测试
public class BridgeTest { public static void main(String[] args) { Bridge bridge = new MyBridge(); /*调用第一个对象*/ Sourceable source1 = new SourceSub1(); bridge.setSource(source1); bridge.method(); /*调用第二个对象*/ Sourceable source2 = new SourceSub2(); bridge.setSource(source2); bridge.method(); } }
1.4、演进过程
举个例子,我们都知道,汽车有不同的发动机,有油的,有电的,还有用天然气的
我们先举两个不采用桥接模式的例子,通过对比,来看出桥接模式的优势
示例一、
项目代码:https://github.com/bjlhx15/patterns.git 中的patterns-base 中 structure/bridge/process01
基础接口
/* * 汽车 */ public interface Car { //安装引擎 void installEngine(); }
先新建以下基础结构
运行测试
@Test public void test() { Car car = new OilBus(); car.installEngine(); }
查看类图结构
可以看到,一种车每增加一种发动机就要增加一个子类,到了后期,子类会越来越多,越来越庞大。这种方法不推荐使用。
示例二、
项目代码:https://github.com/bjlhx15/patterns.git 中的patterns-base 中 structure/bridge/process02
把所有的发动机都定义到Car接口中,每种车实现一次就好了
/* * 汽车 */ public interface Car { //安装燃油引擎 public void installOilEngine(); //安装电动引擎 public void installElectricityEngine(); }
测试
@Test public void test(){ Car bus = new Bus(); Car suv = new Suv(); bus.installOilEngine(); bus.installElectricityEngine(); suv.installOilEngine(); suv.installElectricityEngine(); }
UML图
这种方式就没有那么多子类了,每种车只有一个实现类,但是这种方法不符合我们的开放封闭原则,每增加一种发动机,就要修改Car接口,并修改所有实现类。这种方法肯定也是不推荐的。
示例三、桥接模式
项目代码:https://github.com/bjlhx15/patterns.git 中的patterns-base 中 structure/bridge/impl
创建发动机接口
/* * 发动机 */ public interface Engine { public void installEngine(); }
以及车抽象类
/* * 汽车 */ public abstract class Car { private Engine engine; public Car(Engine engine){ this.engine = engine; } //安装发动机 public abstract void installEngine(); public Engine getEngine() { return engine; } public void setEngine(Engine engine) { this.engine = engine; } }
基础类
UML类图
测试
@Test public void getEngine() { Engine oilEngine = new OilEngine(); Engine electricityEngine = new ElectricityEngine(); Car oilBus = new Bus(oilEngine); Car electricityBus = new Bus(electricityEngine); Car oilSuv = new Suv(oilEngine); Car electricitySuv = new Suv(electricityEngine); oilBus.installEngine(); electricityBus.installEngine(); oilSuv.installEngine(); electricitySuv.installEngine(); }
二、扩展
2.1、JDK中实现的桥接模式
1、 java.sql.Driver、java.sql.DriverManager与java.sql.DriverInfo
mysql的Driver、oracle的Driver就是桥接模式的实现部分。
2、DriverManager与DriverInfo(也就是Driver)实现了桥接模式