将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
桥接模式 类似于多重继承方案,但是多重继承方案往往违背了类得单一职责原则,其复用性比较差,桥接模式 是比多重继承更好的替代方案。
桥接模式 的核心在于 解耦抽象和实现。
注: 此处的抽象并不是指抽象类或接口这种高层概念,实现也不是继承 或 接口实现 。抽象与实现 其实指的是两种独立变化的维度。其中,抽象包含实现,因此,一个抽象类的变化可能涉及到多种维度的变化导致的。
- 抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。
当一个类内部具备两种或多种变化维度时,使用 桥接模式 可以解耦这些变化的维度,使高层代码架构稳定。
抽象和实现分离:这是 桥接模式 的主要特点,也是避免使用 继承 的主要原因。使用 桥接模式,解耦了 抽象 和 实现,使得两者的变化不会对另一方产生影响,使得系统扩展性大大增强。
优秀的扩展能力:桥接模式 的出现就是为了解决多个独立变化的维度的耦合,其高层模块聚合关系已确定(稳定)。因此,无论是 抽象 变化还是 实现 变化,只要对其进行扩展即可,高层代码无需任何更改即可接收扩展。高层代码依赖抽象,严格遵循了 依赖倒置原则。
实现细节对客户透明:由于 桥接模式 在高层模块(抽象层)通过聚合关系构建了稳定的架构(封装)。因此,客户只需与高层模块交互,对 抽象 和 实现 的细节完全不需关注。
桥接模式 由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程,因此会增加系统的理解与设计难度。
【例】视频播放器
需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。
/**
* @author pzz
* @date 2022/5/17
* 视频文件
* 实现化角色
*/
public interface VideoFile {
//解码功能
public void decode(String fileName);
}
avi视频文件(具体的实现化角色)
/**
* @author pzz
* @date 2022/5/17
* avi视频文件
* 具体的实现化角色
*/
public class AviFile implements VideoFile{
@Override
public void decode(String fileName) {
System.out.println("avi视频文件:"+fileName);
}
}
rmv视频文件(具体的实现化角色)
/**
* @author pzz
* @date 2022/5/17
* rmv视频文件
* 具体的实现化角色
*/
public class RmvbFile implements VideoFile{
@Override
public void decode(String fileName) {
System.out.println("rmv视频文件:"+fileName);
}
}
抽象的操作系统类(抽象化角色)
/**
* @author pzz
* @date 2022/5/17
* 抽象的操作系统类
* 抽象化角色
*/
public abstract class OpratingSystem {
//声明视频对象
protected VideoFile videoFile;
public OpratingSystem(VideoFile videoFile) {
this.videoFile = videoFile;
}
public abstract void play(String fileName);
}
Mac操作系统(扩展抽象化角色)
/**
* @author pzz
* @date 2022/5/17
* mac操作系统
* 扩展抽象化角色
*/
public class Mac extends OpratingSystem{
public Mac(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String fileName) {
videoFile.decode(fileName);
}
}
Windows操作系统(扩展抽象化角色)
/**
* @author pzz
* @date 2022/5/17
* Windows操作系统
* 扩展抽象化角色
*/
public class Windows extends OpratingSystem{
public Windows(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String fileName) {
videoFile.decode(fileName);
}
}
测试类
/**
* @author pzz
* @date 2022/5/17
* 测试类
*/
public class Client {
public static void main(String[] args) {
//创建mac系统对象
OpratingSystem system = new Mac(new AviFile());
//使用操作系统播放视频文件
system.play("夏洛特烦恼");
}
}
解释说明:
操作系统和视频文件这是两个维度,当你需要时,可以独立扩展,不会影响原来的代码。
好处:
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
如:如果现在还有一种视频文件类型wmv,我们只需要再定义一个类实现VideoFile接口即可,其他类不需要发生变化。
实现细节对客户透明
共同点:桥接和适配器都是让两个东西配合工作。
不同点:出发点不同。
所以说,如果你拿到两个已有模块,想让他们同时工作,那么你使用的适配器。
如果你还什么都没有,但是想分开实现,那么桥接是一个选择。
两个模式都是为了解决子类过多问题, 但他们的诱因不同:
结束!
当你认为这样是对的,你就应该坚持下去。