定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
结构和说明
示例代码
public class BridgeDemo {
/**
* 定义实现部分的接口,可以与抽象部分接口的方法不一样
*/
public static interface Implementor {
/**
* 示例方法,实现抽象部分需要的某些具体功能
*/
void operationImpl();
}
/**
* 定义抽象部分的接口
*/
public static abstract class Abstraction {
/**
* 持有一个实现部分的对象
*/
protected Implementor impl;
public Abstraction(Implementor impl) {
this.impl = impl;
}
/**
* 示例操作,实现一定的功能,可能需要转调实现部分的具体实现方法
*/
public void operation() {
impl.operationImpl();
}
}
/**
* 真正的具体实现对象
*/
public static class ConcreteImplmentorA implements Implementor {
@Override
public void operationImpl() {
// 真正的实现
}
}
public static class ConcreteImplmentorB implements Implementor {
@Override
public void operationImpl() {}
}
/**
* 扩充由 Abstraction 定义的接口功能
*/
public static class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor impl) {
super(impl);
}
/**
* 示例功能,实现一定的功能
*/
public void otherOperation() {
// 实现一定的功能,可能会使用具体实现部分的实现方法
// 但是本方法更大的可能是使用 Abstraction 中定义的方法
// 通过组合使用 Abstraction 中定义的方法来完成更多的功能
}
}
}
调用顺序
优缺点
优点
- 分离抽象和实现部分
桥接模式分离了抽象部分和实现部分,从而极大的提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。
- 更好的扩展性
由于桥接模式把抽象部分和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立扩展,而不会相互影响,从而大大的提高了系统的可扩展性。
- 可动态的切换实现
由于桥接模式把抽象部分和实现部分分离开了,所以在实现侨接的时候,就可以实现动态的选择和使用具体的实现。也就是说一个实现不是固定的绑在一个抽象接口上了,可以实现运行期间动态的切换。
- 可减少子类的个数
根据前面的讲述,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上可变化数量的乘积个子类;而采用桥接模式来实现,大约需要两个纬度上可变化数量的和个子类。可以明显的减少子类个数。
思考
本质
分离抽象和实现。
何时选用
- 如果你不希望在抽象部分和实现部分采用固定的绑定关系,可以采用桥接模式,来把抽象部分和实现部分分开,然后在程序运行期间来动态的设定抽象部分需要用到的具体的实现,还可以动态的切换具体的实现。
- 如果出现抽象部分和实现部分都能扩展的情况,可以采用桥接模式,让抽象部分和实现部分独立地变化,从而灵活地进行单独扩展,而不是搅在一起,扩展一边就会影响到另一边。
- 如果希望实现部分的修改不会对客户产生影响,可以采用桥接模式。由于客户是面向抽象的接口在运行,实现部分的修改可以独立与抽象部分,并不会对客户产生影响,也可以说对客户是透明的。
- 如果采用继承的实现方案,会导致产生很多子类,对于这种情况,可以考虑采用桥接模式,分析功能变化的原因,看看是否能分离成不同的纬度,然后通过桥接模式来分离它们,从而减少子类的数目。
相关模式
- 桥接模式和策略模式
这两个模式有很大的相似之处。
如果把桥接模式的抽象部分简化来看,暂时不去扩展 Abstraction,这就是去掉 RefinedAbstraction。可以把策略模式的 Context 当做是使用接口的对象,而 Strategy 就是某个接口了,具体的策略实现就是相当于接口的具体实现。这样看来,某些情况下,可以使用桥接模式来模拟实现策略模式的功能。
这两个模式虽然相似,但也还是有区别的。最主要的是模式的目的不一样,策略模式的目的是封装一系列算法,使得它们可以独立地变化。
- 桥接模式和状态模式
由于从模式结构上看,状态模式和策略模式是一样的,因此这两个模式的关系也基本类似于桥接模式和策略模式的关系。只不过状态模式的目的是封装状态对应的行为,并在内部状态改变的时候改变对象的行为。
- 桥接模式和模版方法模式
这两个模式有相似之处。
虽然标准的模版方法模式是采用继承来实现的,但是模版方法也可以通过回调接口的方式来实现。如果把接口的实现独立出去,那就类似于模版方法通过接口去调用去调用具体的实现方法了,这样的结构和简化的桥接模式模式类似了。
可以使用桥接模式来模拟实现模版方法模式的功能。如果在实现 Abstraction 对象的时候,在其中定义方法,方法中就是某个固定的算法骨架,也就是说这个方法就相当于模版方法。在模版方法模式中,是把不能确定实现的步骤延迟到子类去实现;现在在桥接模式中,把不能确定实现的步骤委托给具体实现部分去完成,通过回调实现部分的接口,来完成算法骨架中的某些步骤。这样一来,就可以实现使用桥接模式来模拟模版方法模式的功能。
使用桥接模式来模拟实现模版方法模式的功能,还有一个潜在的好处,就是模版方法也可以很方便地扩展和变化。在标准的模版方法中,一个问题就是当模版发生变化的时候,所有子类都要变化,非常不方便。而使用桥接模式来实现类似的功能,就没有这个问题。
- 桥接模式和抽象工厂模式
这两个模式可以组合使用。
桥接模式中,抽象部分需要获取相应的实现部分的接口对象,那么是谁来创建实现部分的具体实现对象呢?这就是抽象工厂模式派上用场的地方。也就是说使用抽象工厂模式来创建和配置一个特定的具体的实现对象。
事实上,抽象工厂是用来创建一系列对象的,如果创建的对象很少,或者是很简单,还可以采用简单工厂,也能达到通常的效果,但是会比抽象工厂来得简单。
- 桥接模式和适配器模式
这两个模式模式可以组合使用。
这两个模式功能完全不一样,适配器模式的功能主要是帮助无关的类协同工作,重点在解决由于接口不兼容而不能在一起工作的类,使得它们可以一起工作。而桥接模式则重点在分离抽象部分和实现部分。
所以在使用上,通常在系统完成设计之后,才会考虑使用适配器模式;而桥接模式在系统开始设计的时候就要开始考虑使用。
虽然功能不一样,这两个模式还是可以组合使用的,比如,已有实现部分的接口,但是不太适应现在新功能对接口的需要,完全抛弃吧,有些功能还用得上,该怎么办呢?那就得用适配器来适配,使得旧的接口能够适应新的功能的需要。