定义:将抽象部分与它的实现部分分离,使它们可以独立的变化。
类型:对象结构型模式
类图:
桥接模式的结构
- Abstraction :抽象类的接口,维护一个指向Implementor类型对象的指针,在本例中为 IPluginPlatform。
- RefinedAbstraction :扩充Abstraction定义的接口,在本例中为 PluginVersionA、PluginVersionB。
- Implementor :定义实现类的接口,该接口不一定要与Abstraction 完全一致;事实上,这两个接口可以完全不同。一般来讲 Implementor 接口仅提供基本操作,而Abstraction 则定义了基于这些基本操作的较高层次的操作。
- ConcreteImplementor :实现Implementor 并定义它的具体实现。
示例
假设要为一个系统设计插件,首先这个系统是需要跨平台的,其次,由于插件经常会版本升级,所以系统需要支持多多个版本插件。顺着需求,我们可能会这样去考虑解决方案。
因为需要跨平台,那么通过抽象和继承来实现跨平台。
因为需要跨平台,那么通过抽象和继承来实现跨平台。
又因为每个平台都需要支持多个插件版本,那就继续在每个平台下扩展不同版本吧。
代码如下:
abstract class IPlugin {
public abstract void loadPlugin();
public abstract void unloadPlugin();
public void start() {
// add operation
}
public void stop() {
// add operation
}
}
class WindowsPlugin extends IPlugin {
@Override
public void loadPlugin() {
}
@Override
public void unloadPlugin() {
}
}
class WinPluginVer1 extends WindowsPlugin {
@Override
public void start() {
loadPlugin();
super.start();
}
@Override
public void stop() {
unloadPlugin();
super.stop();
}
}
class WinPluginVer2 extends WindowsPlugin {
@Override
public void start() {
loadPlugin();
super.start();
}
@Override
public void stop() {
unloadPlugin();
super.stop();
}
}
这里省略 windows以外平台, 以及 Ver1,2以外版本的代码。
以上确实是一种解决方法,但却不是一种好方法。试想一下如果现在又多出一种平台了怎么办?只能再加一个继承分支,同时对这个平台也需要追加多个版本的实现。而如果又升级了一个版本怎么办?只能在每种平台下都追加该版本的实现。类结构就膨胀成如下这样:
以上确实是一种解决方法,但却不是一种好方法。试想一下如果现在又多出一种平台了怎么办?只能再加一个继承分支,同时对这个平台也需要追加多个版本的实现。而如果又升级了一个版本怎么办?只能在每种平台下都追加该版本的实现。类结构就膨胀成如下这样:
现在可以看出,平台和版本这系统中两个层次的实现被耦合在一起,导致每次功能追加都伴随着重复的实现和类的膨胀。很难想象,当再追加 n个平台,和 n个版本的支持后,系统结构会爆炸式的膨胀成什么样!伴随着的还有设计,编码,测试等成本以及风险的成倍增加。
Bridge模式实现代码
//抽象类接口 --> 插件
abstract class IPlugin{
protected IPluginPlatform platform;
public void setPlatform(IPluginPlatform platform) {
this.platform = platform;
}
public abstract void start();
public abstract void stop();
}
//不同 版本号 实现
class PluginVersionA extends IPlugin{
@Override
public void start() {
platform.loadPlugin();
}
@Override
public void stop() {
platform.unloadPlugin();
}
}
class PluginVersionB extends IPlugin{
@Override
public void start() {
platform.loadPlugin();
}
@Override
public void stop() {
platform.unloadPlugin();
}
}
//抽象类接口 --> 平台
abstract class IPluginPlatform{
public abstract void loadPlugin();
public abstract void unloadPlugin();
}
//Windows平台实现
class WindowsPlugin extends IPluginPlatform{
@Override
public void loadPlugin() {
System.out.println("WindowsPlugin loading ...");
}
@Override
public void unloadPlugin() {
System.out.println("WindowsPlugin unLoading ...");
}
}
class LinuxPlugin extends IPluginPlatform{
@Override
public void loadPlugin() {
System.out.println("LinuxPlugin loading ...");
}
@Override
public void unloadPlugin() {
System.out.println("LinuxPlugin unLoading ...");
}
}
//客户端调用
public class BridgeClient {
/**
* @param args
*/
public static void main(String[] args) {
IPlugin plugin = new PluginVersionA();
IPluginPlatform platform = new WindowsPlugin();
plugin.setPlatform(platform);
plugin.start();
plugin.stop();
}
}
桥接模式适用场景
- 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户端的代码不必重新编译。
- (C++)你想对客户完全隐藏抽象的实现部分。
- 你想在多个实现间 共享实现,但同时要求客户并不知道这一点。
桥接模式的优点
- 分离接口及其实现部分。一个实现未必不变地邦定在一个接口上。抽象类的实现可以在运行的时刻进行配置,一个对象甚至可以在运行的时候改变它的实现。
将Abstraction和Implementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。
另外接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abstraction和Implementor即可。
- 提高可扩充性。你可以独立地对Abstraction和Implementor层次结构进行扩充。
- 实现细节对客户的透明。你可以对客户隐藏实现细节。