1. 意图
将抽象部分与它的实现部分分离,使它们都可以独立地变化
2. 动机
- 需求
栗子的公司接到一个需求,要开发一个跨平台,支持多种格式的图片浏览系统,要求支持jpg,png,bmp等格式,可以在Windows,Linux,MacOS等操作系统运行;扩展性要好,能够支持新的图片格式和操作系统
- v1.0方案
栗子很快提出了一个方案
v1.0方案中使用多层次继承来解决多种类,跨平台的问题;image是图像顶层接口,不同类型的图像作为其直接实现;具体的绘制方法draw()作为抽象方法,由不同操作系统去实现;当新增图片类型的时候,需要实现image接口,并且操作系统需要去单独实现draw()方法;这样做完后发现,如果有m种图片类型,n种系统,则需要创建m*n个具体实现类,来实现draw()方法,违反了合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP);所以客户要求栗子改进方案
- v2.0方案
操作系统和图像类型是变化的两个维度,v1.0方案中,只是将图像类型抽象出了接口,以应对不同类型的图像类型变化,而对操作系统,则没有这么做;v2.0需要将图像类型也抽象出接口
3. 适用性
- 当一个类具有多个独立变化的维度时,且这些维度都需要进行扩展
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
4. 结构
5. 参与者
- 抽象化角色:将多个维度中的一个维度(通常是能够体现业务主要特征的维度)作为一个抽象类,剩余的维度作为实现化角色接口,并在抽象化角色中添加引用
- 抽象化角色的扩展:将抽象化维度进行扩展
- 实现化角色:是接口,定义实现维度的规范
- 实现化角色的扩展:实现化角色的具体实现
6. 代码示例
- 抽象化角色
package com.lt.v02.abstraction;
import com.lt.v02.implementor.IDrawer;
/**
* 抽象化角色:业务主要维度作为抽象化角色,即图像类型
* @author lt
* @date 2019年4月17日
* @version v1.0
*/
public abstract class AbstractImage {
private IDrawer iDrawer;
public void setDrawer(IDrawer iDrawer){
this.iDrawer = iDrawer;
}
public void showImage(){
iDrawer.draw();
}
}
- 抽象化角色的扩展
package com.lt.v02.abstraction.refined_abstraction;
import com.lt.v02.abstraction.AbstractImage;
/**
* @author lt
* @date 2019年4月17日
* @version v1.0
*/
public class BmpImage extends AbstractImage {
@Override
public void showImage() {
super.showImage();
System.out.println("类型是bmp");
}
}
package com.lt.v02.abstraction.refined_abstraction;
import com.lt.v02.abstraction.AbstractImage;
/**
* @author lt
* @date 2019年4月17日
* @version v1.0
*/
public class JpgImage extends AbstractImage {
@Override
public void showImage() {
super.showImage();
System.out.println("类型是jpg");
}
}
- 实现化角色
package com.lt.v02.implementor;
/**
* @author lt
* @date 2019年4月17日
* @version v1.0
*/
public interface IDrawer {
public void draw();
}
- 实现化角色的扩展
package com.lt.v02.implementor.concrete_implementor;
import com.lt.v02.implementor.IDrawer;
/**
* @author lt
* @date 2019年4月17日
* @version v1.0
*/
public class LinuxDrawer implements IDrawer {
@Override
public void draw() {
System.out.print("Linux中显示图像:");
}
}
package com.lt.v02.implementor.concrete_implementor;
import com.lt.v02.implementor.IDrawer;
/**
* @author lt
* @date 2019年4月17日
* @version v1.0
*/
public class WinDrawer implements IDrawer {
@Override
public void draw() {
System.out.print("windows中显示图像:");
}
}
7. 已知应用
jdk中java.util.logging包中的Handler(抽象化角色)和Formatter(实现化角色)
8. 相关模式
- 抽象工厂模式可以用来创建和配置一个特定的桥接模式。
- 适配器模式用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然
而,适配器模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。
9. 疑问
- 我将桥接模式中的抽象化和实现理解为两个变化的维度,这样理解是否正确?
- 桥接模式的桥梁即顶层的主要业务维度的抽象类?