桥接 Bridge
桥接模式是一种结构型设计模式,可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构,从而能在开发时分别使用。
层次结构:
- 功能层次: 子类添加父类没有的功能,按功能划分子类,属于桥接模式的抽象。
- 实现层次: 子类实现父类的方法,按实现划分子类,属于桥接模式的实现。
为什么要使用?
桥接模式的对象职责:
将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构。 或者说把一个类按功能和实现两个维度划分,用组合关系关联起来。
在现实世界,许多事物都有两个或以上维度的变化。那么当我们将它们抽象成对象的时候,就需要注意处理其中的变化。
例如几何体这个抽象类,如果我们只按形状划分的话,可以分为正方体⬜、球体⚪等。但是如果后期需要拓展几何体,给它添加颜色。那么又要在正方体和球体下各自添加子类红色🟥🔴、绿色🟩🟢等。层级变深,代码也变臃肿。这时桥接模式🌉就可以派上它的作用。
我们将几何体分为形状和颜色两个维度,再用组合关系连接起来。那么它们就可以互相组合。降低了继承关系带来的强耦合。未来要新增形状或颜色只需要在各自抽象类或接口中继承或实现即可。类似的例子还有很多,如不同平台的 GUI
层。抽象部分为 GUI
层可以随意添加功能,实现部分为操作系统的 API
可以更换各种平台。
但是使用桥接模式对于我们的抽象能力有更大的要求,要求我们能明确类的维度。从整体结构设计类。
模式结构
-
抽象部分(Abstraction)定义的一种抽象分类。提供高层控制逻辑,依赖于完成底层实际工作的实现对象。抽象部分可以列出和实现部分一样的方法,但是抽象部分通常声明一些复杂行为,这些行为依赖于多种由实现部分声明的原语操作。
-
实现部分(Implementation)定义抽象实体中具备的多种行为。为所有具体实现声明通用接口。抽象部分仅能通过在这里声明的方法与实现对象交互。
-
精确抽象(Refined Abstraction)继承抽象实体的子类实体。提供控制逻辑的变体。与其父类一样,它们通过通用实现接口与不同的实现进行交互。
-
具体实现(Concrete Implementations)实现抽象行为的具体算法。
通常情况下,客户端 (Client) 仅关心如何与抽象部分合作。但是,客户端需要将抽象对象与一个实现对象连接起来。
桥接模式的类图:
桥接模式使得 RefinedAbstraction
和 ConcreteImplementations
可以自由组合,从而降低了代码的耦合度。
模式实现
该示例使用桥接模式将几何体分为形状和颜色两个维度。将形状作为几何体的主要特征,让不同子类扩展不同的方法(即添加父类没有的功能);将获取颜色的行为抽离成一个新的接口,交由子类去实现(即实现父类的方法)。
示例程序的类图
代码实现
抽象部分
package example.bridge;
/** 几何体抽象部分 */
public abstract class Geometry {
/** 几何体颜色-组合关系 */
private Color color;
public Geometry(Color color) {
this.color = color;
}
/** 打印几何体 */
public void print() {
System.out.println("这是一个" + color.getColor() + "的" + getShape());
}
/**
* 获取几何体形状
* @return 几何体形状
*/
public abstract String getShape();
}
实现部分
package example.bridge;
/** 颜色实现部分 */
public interface Color {
/**
* 获取颜色
* @return 颜色字符串
*/
String getColor();
}
精准抽象
package example.refined;
import example.bridge.Color;
import example.bridge.Geometry;
/** 正方体精准抽象 */
public class Cube extends Geometry {
public Cube(Color color) {
super(color);
}
/**
* 获取几何体形状
* @return 几何体形状
*/
@Override
public String getShape() {
return "立方体";
}
/** 搭积木-立方体特有方法 */
public void buildBlocks() {
System.out.println("立方体可以搭积木!");
}
}
package example.refined;
import example.bridge.Color;
import example.bridge.Geometry;
/** 球体精准抽象 */
public class Sphere extends Geometry {
public Sphere(Color color) {
super(color);
}
/**
* 获取几何体形状
* @return 几何体形状
*/
@Override
public String getShape() {
return "球体";
}
/** 滚动-球体特有方法 */
public void roll() {
System.out.println("球体可以滚动!");
}
}
具体抽象
package example.impl;
import example.bridge.Color;
/** 红色具体实现 */
public class Red implements Color {
/**
* 获取颜色
* @return 颜色字符串
*/
@Override
public String getColor() {
return "红色";
}
}
package example.impl;
import example.bridge.Color;
/** 红色具体实现 */
public class Red implements Color {
/**
* 获取颜色
* @return 颜色字符串
*/
@Override
public String getColor() {
return "红色";
}
}
代码测试
import example.bridge.Geometry;
import example.impl.Green;
import example.impl.Red;
import example.refined.Cube;
import example.refined.Sphere;
/** 测试桥接模式 */
public class Test {
public static void main(String[] args) {
Red red = new Red();
Green green = new Green();
System.out.println("自由组合形状和颜色:");
Geometry redCube = new Cube(red);
Geometry redSphere = new Sphere(red);
Geometry greenCube = new Cube(green);
Geometry greenSphere = new Sphere(green);
redCube.print();
redSphere.print();
greenCube.print();
greenSphere.print();
System.out.println("\n--------------------- 分割线 ---------------------\n");
System.out.println("不同抽象实体功能:");
Cube cube = new Cube(red);
Sphere sphere = new Sphere(red);
cube.buildBlocks();
sphere.roll();
}
}
输出结果
自由组合形状和颜色:
这是一个红色的立方体
这是一个红色的球体
这是一个绿色的立方体
这是一个绿色的球体
--------------------- 分割线 ---------------------
不同抽象实体功能:
立方体可以搭积木!
球体可以滚动!
从测试和结果来看,使用桥接模式让类变得更加灵活。
常用场景和解决方案
- 拆分或重组一个具有多重功能的庞杂类。桥接模式能让你将它按不同层次划分。
- 多个维度扩展一个类。例如,几何体的形状和颜色维度。
- 如果你需要在运行时切换不同实现方法,可使用桥接模式。
- 需要提供平台独立性的应用程序时。
- 需要在某种统一协议下增加更大组件时。
- 基于消息驱动的场景,例如,手机短信、微信消息等。
- 实现上传到云存储时,将操作和执行器分开,即基本上传操作和云存储服务商执行器分开。
模式的优缺点
优点 | 缺点 |
---|---|
你可以创建与平台无关的类和程序。 | 对高内聚的类使用该模式可能会让代码更加复杂。 |
客户端代码仅与高层抽象部分进行互动,不会接触到平台的详细信息。 | |
开闭原则。你可以新增抽象部分和实现部分,且它们之间不会相互影响。 | |
单一职责原则。抽象部分专注于处理高层逻辑,实现部分处理平台细节。 |
使用桥接模式的优势
- 分离抽象实体与行为,可以提升各种维度的演化效率。
- 用组合关系替代了多重继承,提高了代码结构的演化灵活性。
- 符合表达原则,提升代码的可理解性。
使用桥接模式的劣势
- 增加了维护成本。
- 导致性能下降。
- 增加设计难度。
拓展知识
- 桥接模式通常会于开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。另一方面,适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。
- 你可以结合使用生成器模式和桥接模式:主管类负责抽象工作,各种不同的生成器负责实现工作。
🔙 设计模式
📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!
🔗参考文献:
▶️ bilibili-趣学设计模式;黄靖锋. --拉勾教育
📖 图解设计模式 /(日)结城浩著;杨文轩译. --北京:人民邮电出版社,2017.1