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)来控制设备。
同样设备接口使得遥控器代码可以复用于遥控多种不同的设备,而继承控制器父类,又可以实现不同的遥控器。
参考文献: