7. 这就是桥接模式 - 图1

1. 引言

桥接模式理解起来也是非常简单,我们仍然从生活中的问题出发,如果一些事物的分类可以从两个或者多个维度来划分,就比如不同品牌和不同排量的汽车,他们可以有 M x N 种结果(例如:奥迪A8 2.0排量,奥迪A6 2.0排量,奔驰S350L 3.0排量,等等)

这种情况下如果选择继承的方式,就会出现一种多层继承的关系,子类就会非常多,同时扩展也很麻烦
7. 这就是桥接模式 - 图2

像这样的例子还有很多,比如品牌和产品类型之间,或者不同颜色和字体的图形或者文字,等等

2. 桥接模式引入

还是老规矩,直接用实际的代码例子引入

背景:相机品牌(索尼,佳能等)和相机类型(单反,微单,卡片机等)两种维族的组合结果

我们想要做的就是取消他们的继承关系,而使用组合

  • 先创建一个抽象的相机品牌类

    1. /**
    2. * 相机品牌类
    3. */
    4. public interface CameraBrand {
    5. void showInfo();
    6. }
  • 接着就是两个具体的实现类,这里举了索尼和佳能两个品牌

    1. /**
    2. * 索尼品牌
    3. */
    4. public class Sony implements CameraBrand {
    5. @Override
    6. public void showInfo() {
    7. System.out.print("【索尼】");
    8. }
    9. }
    1. /**
    2. * 佳能品牌
    3. */
    4. public class Canon implements CameraBrand {
    5. @Override
    6. public void showInfo() {
    7. System.out.print("【佳能】");
    8. }
    9. }
  • 下面再将相机产品类抽象出来,为了实现组合,引入相机品牌成员,为了能让在子类中也能访问到,我用了 protected

    1. /**
    2. * 抽象相机类
    3. */
    4. public abstract class Camera {
    5. // 将品牌组合进来
    6. protected CameraBrand cameraBrand;
    7. public Camera(CameraBrand cameraBrand) {
    8. this.cameraBrand = cameraBrand;
    9. }
    10. public void showInfo(){
    11. cameraBrand.showInfo();
    12. }
    13. }
  • 下面就是两个相机的产品具体类型

    1. /**
    2. * 单反相机
    3. */
    4. public class SlrCameras extends Camera {
    5. public SlrCameras(CameraBrand cameraBrand) {
    6. super(cameraBrand);
    7. }
    8. @Override
    9. public void showInfo() {
    10. super.showInfo();
    11. System.out.println("单反相机");
    12. }
    13. }
    1. /**
    2. * 卡片相机(数字相机)
    3. */
    4. public class DigitalCamera extends Camera {
    5. public DigitalCamera(CameraBrand cameraBrand) {
    6. super(cameraBrand);
    7. }
    8. @Override
    9. public void showInfo() {
    10. super.showInfo();
    11. System.out.println("卡片相机(数字相机)");
    12. }
    13. }

    测试一下

    1. public class Test {
    2. public static void main(String[] args) {
    3. // 索尼单反相机
    4. Camera camera = new SlrCameras(new Sony());
    5. camera.showInfo();
    6. // 佳能卡片相机
    7. Camera camera2 = new DigitalCamera(new Canon());
    8. camera2.showInfo();
    9. }
    10. }

    运行结果:

【索尼】单反相机
【佳能】卡片相机(数字相机)

从上述代码中可以看出,我们现在已经可以对不同类型和不同产品的相机进行任意的组合了,例如索尼单反相机,或者佳能单反相机都是可以的,如果想要增加一个品牌或者产品只需要增加具体的实现类就可以了

3. 桥接模式理论

3.1 定义和特点

定义:将抽象与实现分离,使它们可以独立变化

实现的意思并不是指抽象的派生类,而是指通过组合来代替继承关系,从而降低抽象和具体实现产品两个可变换维度之间的耦合,就像我们的相机品牌和相机产品类型之间的分离

3.2 结构

7. 这就是桥接模式 - 图3
简单说明一下其中的角色

  • 实现化角色(Implementor):定义实现化角色的接口,供扩展抽象化角色使用
    • 例如抽象出相机的品牌,可以扩展出例如佳能索尼等各种品牌
  • 具体实现化角色(ConcreteImplementor):实现化角色的具体实现
    • 例如各种品牌
  • 抽象化(Abstraction)角色:定义一个抽象类,其中引用了实现化角色(想要组合)
    • 例如相机产品
  • 扩展抽象化(RefinedAbstraction)角色:抽象化角色子类,实现父类方法,且通过组合关系调用实现化角色中的业务方法
    • 例如具体相机产品,单反相机,卡片相机等等

      3.3 优点和缺点

      优点:
      经常遇到一些可以通过两个或多个维度划分的事物,第一种解决方式就是多层继承,但是复用性比较差,同时类的个数也会很多,桥接模式是改进其的更好办法

桥接模式增强了系统的扩展性,在两个维度中扩展任意一个维度都不需要修改原有代码,符合开闭原则

缺点:
桥接模式增加了系统的理解与设计难度:因为聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度

3.4 使用场景

  • 一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
    • 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合
  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展
  • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者
  • 不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统