何为桥接模式?举个例子,假如需要大、中、小三种型号的蜡笔,且每种型号的笔都能绘制12种不同的颜色,那就需要36支笔。如果是毛笔,那就只需要3种型号的笔,和12种颜色的调色板。如果新增一种型号的笔,蜡笔还需要新增12支,而毛笔只需一支,因为有调色板。在蜡笔中,颜色和型号两个不同的变化维度耦合在一起,不管对哪一个进行扩展,都会影响另外一个维度。而毛笔不同,不管是扩展型号还是颜色,不会影响另外一个。蜡笔存在较强的耦合性,毛笔将二者解耦,使用起来灵活,扩展也方便。
定义
将抽象部分与它的实现部分解耦,使得二者都可以独立变化。
结构
Abstraction(抽象类)
它是用于定义抽象类的接口,通常是抽象类而不是接口,其中定义了Implementor(实现类接口)类型的对象并维护它,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
RefinedAbstraction(扩充抽象类)
它扩充由Abstraction定义的接口,通常情况下,它不再是抽象类,而是具体类,实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
Implementor(实现类接口)
它是定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同。一般而言,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了声明,而具体实现交给了其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来代替继承关系。
ConcreteImplemtor(具体实现类)
它具体实现了Implementor接口,在不同的ConcreteImplemtor中提供基本操作的不同实现,在程序运行时ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。
举例说明
通过一个不同系统,显示不同格式图片的例子来进行说明这个设计模式。不同操作系统,显示图片的方法不同。
1.需要一个辅助类,该类不用实现,这里只是模拟这样一个过程
// 辅助类 - 各种格式的图像文件最终转换成像素矩阵
public class Matrix {
// xxx
}
2.实现接口类和具体实现类
/**
* 实现接口类(Implementor) - 操作系统接口
* 提供显示图像的方法
*/
public interface Os {
/**
* 显示像素矩阵m
* @param m
*/
public void doPaint(Matrix m);
}
/**
* 具体实现类 - Windows系统
* 实现Windows中显示图像的方法
*/
public class WindowsImp implements Os{
@Override
public void doPaint(Matrix m) {
System.out.println("在Windows中显示图像:");
}
}
/**
* 具体实现类 - Linux系统
* 实现Linux中显示图像的方法
*/
public class LinuxImp implements Os{
@Override
public void doPaint(Matrix m) {
System.out.println("在Linux中显示图像:");
}
}
3.抽象类和扩充抽象类
// 抽象类(Abstraction) - 抽象图像类
public abstract class Image {
// 操作系统
protected Os os;
public void setOs(Os os) {
this.os = os;
}
/**
* 解析文件
* @param fileName 文件名
*/
public abstract void parseFile(String fileName);
}
// 扩充抽象类 - PNG格式图像类
public class PNGImage extends Image{
@Override
public void parseFile(String fileName) {
// 模拟解析PNG文件
Matrix m = new Matrix();
os.doPaint(m);
System.out.println(fileName + ",格式为PNG.");
}
}
//扩充抽象类 - JPG格式图像类
public class JPGImage extends Image{
@Override
public void parseFile(String fileName) {
// 模拟解析PNG文件
Matrix m = new Matrix();
os.doPaint(m);
System.out.println(fileName + ",格式为JPG.");
}
}
4.测试类
public class Client {
public static void main(String[] args) {
// Windows操作系统
Os os = new WindowsImp();
// PNG格式
Image img = new PNGImage();
img.setOs(os);
img.parseFile("Vae");
System.out.println("--------------");
// Linux操作系统
os = new LinuxImp();
// JPG格式
img = new JPGImage();
img.setOs(os);
img.parseFile("Jay");
}
}
5.结果截图
优点
1.分离抽象接口及其实现部分。解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。也就是说抽象和实现不在同一个继承层次结构中,而是“子类化”他们,使它们各自具有自己的子类,以便以任意组合。
2.很多情况下,它取代多层继承方案,极大减少了子类个数。
3.提高了系统的可扩展性
缺点
1.会增加系统的理解与设计难度,关联关系建立在抽象层
2.使用范围具有一定局限性,如何正确识别两个维度需要一定的经验积累。
在软件开发中,适配器模式通常和桥接模式联合使用。