在正式介绍桥接模式之前,我先跟大家谈谈两种常见文具的区别,它们是毛笔和蜡笔。假如我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3×12 = 36支,但如果使用毛笔的话,只需要提供3种型号的毛笔,外加12个颜料盒即可,涉及到的对象个数仅为 3 + 12 = 15,远小于36,却能实现与36支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有12种颜色,对应的蜡笔需增加12支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响。如果使用软件工程中的术语,我们可以认为在蜡笔中颜色和型号之间存在较强的耦合性,而毛笔很好地将二者解耦,使用起来非常灵活,扩展也更为方便。在软件开发中,我们也提供了一种设计模式来处理与画笔类似的具有多变化维度的情况,即本章将要介绍的桥接模式。
桥接模式是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。与多层继承方案不同,它将两个独立变化的维度设计为两个独立的继承等级结构,并且在抽象层建立一个抽象关联,该关联关系类似一条连接两个独立继承结构的桥,故名桥接模式。
桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。桥接定义如下:
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
个人觉得,桥接模式最重要的地方是将不同维度的变化抽离出来,从而达到解耦的效果
在使用桥接模式时,我们首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。通常情况下,我们将具有两个独立变化维度的类的一些普通业务方法和与之关系最密切的维度设计为“抽象类”层次结构(抽象部分),而将另一个维度设计为“实现类”层次结构(实现部分)。例如:对于毛笔而言,由于型号是其固有的维度,因此可以设计一个抽象的毛笔类,在该类中声明并部分实现毛笔的业务方法,而将各种型号的毛笔作为其子类;颜色是毛笔的另一个维度,由于它与毛笔之间存在一种“设置”的关系,因此我们可以提供一个抽象的颜色接口,而将具体的颜色作为实现该接口的子类。在此,型号可认为是毛笔的抽象部分,而颜色是毛笔的实现部分,结构示意图如图10-4所示:
在图10-4中,如果需要增加一种新型号的毛笔,只需扩展左侧的“抽象部分”,增加一个新的扩充抽象类;如果需要增加一种新的颜色,只需扩展右侧的“实现部分”,增加一个新的具体实现类。扩展非常方便,无须修改已有代码,且不会导致类的数目增长过快。
假设笔有2个维度:笔的类型和颜色
那么就应该将这2个维度分开,达到解耦的效果。
/**
* 笔
* @author huangy on 2019-10-21
*/
public abstract class Pen {
private Color color;
/**
* 可以给这支笔设置颜色
*/
public void setColor(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
/**
* 绘图
*/
public abstract void draw();
}
/**
* 圆珠笔
* @author huangy on 2019-10-21
*/
public class BallPen extends Pen {
@Override
public void draw() {
System.out.println("BallPen draw, and my color is " + getColor());
}
}
/**
* 颜色
* @author huangy on 2019-10-21
*/
public interface Color {
}
/**
* 红色
* @author huangy on 2019-10-21
*/
public class Red implements Color {
@Override
public String toString() {
return "red";
}
}
public class PenDemo {
public static void main(String[] args) {
Pen pen = new BallPen();
pen.setColor(new Red());
pen.draw();
}
}
###图片解析器示例
展示了图片和操作系统2个抽象层次利用桥接模式来解耦,
首先是图片抽象层次
/**
*
* 抽象图像类:抽象类
* @author huangy on 2019-06-09
*/
public abstract class Image {
protected ImageImp imp;
public void setImageImp(ImageImp imp) {
this.imp = imp;
}
public abstract void parseFile(String fileName);
}
/**
* BMP格式图像:扩充抽象类
* @author huangy on 2019-06-09
*/
public class BMPImage extends Image {
public void parseFile(String fileName) {
//模拟解析BMP文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为BMP。");
}
}
/**
* PNG格式图像:扩充抽象类
* @author huangy on 2019-06-09
*/
class PNGImage extends Image {
public void parseFile(String fileName) {
//模拟解析PNG文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为PNG。");
}
}
然后是系统抽象层次
/**
* 抽象操作系统实现类:实现类接口
* @author huangy on 2019-06-09
*/
public interface ImageImp {
/**
* 显示像素矩阵
*/
void doPaint(Matrix m);
}
/**
* Linux操作系统实现类:具体实现类
* @author huangy on 2019-06-09
*/
public class LinuxImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Linux系统的绘制函数绘制像素矩阵
System.out.print("在Linux操作系统中显示图像:");
}
}
/**
* Windows操作系统实现类:具体实现类
* @author huangy on 2019-06-09
*/
public class WindowsImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Windows系统的绘制函数绘制像素矩阵
System.out.print("在Windows操作系统中显示图像:");
}
}
测试一下:
public class DemoTest {
public static void main(String[] args) {
Image image = new BMPImage();
image.setImageImp(new LinuxImp());
image.parseFile("测试文件名字");
}
}
https://blog.csdn.net/LoveLion/article/details/7464195