把书读薄 | 《设计模式之美》设计模式与范式(结构型-桥接模式)

1,968 阅读4分钟

0x0、引言

日常更新,忙里偷闲啃《设计模式之美》,本文对应设计模式与范式:结构型(49),桥接模式 (Bridge Pattern)。

上节学了第一种结构型设计模式 → 代理模式在不改变原始类(被代理类)的情况下,通过引入代理类来给原始类附加功能。

而桥接模式有两种理解方式:

  • 将抽象和实现解耦,让它们能独立开发;(应用场景较少)
  • 用抽象关联取代多层继承,将类间的继承关系转换为动态的对象组合关系; (用得较多,避免了多层继承类爆炸问题)

桥接模式可以说是DIP原则(依赖反转)的具体实践,从依赖一个大而全的对象 → 依赖两个可以独立变化的维度。

说的有点抽象,没关系,等下写个例子就懂了~

Tips:二手知识加工难免有所纰漏,感兴趣有时间的可自行查阅原文,谢谢。


0x1、举个例子

比如,现在有一个圆形和矩形,它们可以抽象出形状这个父类,然后它们对应两个子类:

abstract class Shape {
    abstract void show();
}

public class Circle extends Shape {
    @Override void show() { System.out.println("圆形"); }
}

public class Square extends Shape {
    @Override void show() { System.out.println("矩形"); }
}

如果加入了变化:颜色,有红色和蓝色,跟形状组合组合成下面四种父类:

public class RedCircle extends Shape {
    @Override void show() { System.out.println("红色圆形"); }
}

public class RedSquare extends Shape {
    @Override void show() { System.out.println("红色矩形"); }
}

public class BlueCircle extends Shape {
    @Override void show() { System.out.println("蓝色圆形"); }
}

public class BlueSquare extends Shape {
    @Override void show() { System.out.println("蓝色矩形"); }
}

如果增加了形状三角形和椭圆形,那么此时子类数量变成4 * 2 = 8,如果再在增加颜色金色,那么此时子类数量变成4 * 3 = 12,形状和颜色的加多,类就炸了!

引入桥接模式可以解决这种问题,这里的两个变化维度很清晰:形状 & 颜色,我们把前者理解为 抽象部分,后者理解为 实现部分,在它们之间搭座桥。

实现代码如下:

// 颜色 → 实现部分
public interface IColor {
    String draw();
}

// 红色、蓝色 → 实现部分具体实现
public class Red implements IColor {
    @Override public String draw() { return "红色"; }
}

public class Blue implements IColor {
    @Override
    public String draw() { return "蓝色"; }
}

// 形状 → 抽象部分
abstract class Shape {
    // 形状持有颜色引用,颜色引用通过构造函数注入,这就是桥接过程
    protected IColor color;
    public Shape(IColor color) { this.color = color; }
    abstract void show();
}

// 圆型、矩形 → 抽象部分扩展
public class Circle extends Shape {
    public Circle(IColor color) { super(color); }
    @Override void show() { System.out.println(color.draw() + "圆形"); }
}

public class Square extends Shape {
    public Square(IColor color) { super(color); }
    @Override void show() { System.out.println(color.draw() + "矩形"); }
}

// 测试用例
public class CircleTest {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new Red());
        Shape blueSquare = new Square(new Blue());
        redCircle.show();
        blueSquare.show();
    }
}

运行输出结果如下

例子很好懂,顺带引出四个角色的介绍,以及桥接模式的应用场景~


0x2、四个角色与应用场景

  • Implementor(实现角色):实现部分的接口,可以理解成定义抽象行为;
  • ConcreteImplementor(具体实现角色):实现抽象行为的具体算法;
  • Abstraction(抽象角色):定义一种抽象分类;
  • Refined Abstraction(扩展抽象角色):继承扩展具体的角色;

桥接模式的原理核心

抽象与抽象的分离,具体的实现类依赖抽象而不是依赖具体,简介完成了具体类与具体类间的解耦,它们之间使用抽象来进行组合或聚合,而不再使用继承。

应用场景

  • 1、类存在两个或多个独立变化的维度,而且都需要独立进行扩展;
  • 2、不希望使用继承或因多层继承,导致系统内类个数的急剧增加;
  • 3、需要在某种统一协议下增加更多组件(如支付场景,期望支持微信、支付宝等支付组件,统一协议就是收款、支付、扣款);
  • 4、基于消息驱动的场景(消息行为比较统一,包括发送、接收、处理和回执,但不同APP的实现通常各不相同);
  • 5、需要提供平台独立性的应用程序(如不同数据库的JDBC驱动程序、硬盘驱动程序等);

优点:分离实体与行为,更好的可扩展性,代替多层继承方案,极大减少子类数量; 缺点

  • 增加设计难度:一开始就要针对抽象层进行,正确识别两个独立维度需要一定的经验积累;
  • 增加维护成本:组合和聚合不像继承那样容易找到对象简单的调用关系;
  • 导致性能下降:组合或聚合关系在OOP中使用委托的事项方式,调用对象变多,自然影响程序性能;

小彩蛋~

开头的电器/设备怎么跟遥控器建立联系呢?↓↓↓

遥控器父类(Remote)包含一个设备的引用(device),所有遥控器都可以通过通用的设备接口(Device Interface)来控制设备。 同样设备接口使得遥控器代码可以复用于遥控多种不同的设备,而继承控制器父类,又可以实现不同的遥控器。


参考文献: