桥接模式(Bridge):把抽象和实现解耦,让它们各自独立演化
桥接模式,说实话,是我在实际项目中用得比较多的一个结构型模式。它解决了一个很本质的问题:当抽象和实现都可能变化时,怎么避免类爆炸。
我记得有一次接手一个老项目,里面光消息发送的类就有十几个:短信发送、邮件发送、微信发送,每种又分普通消息、紧急消息、定时消息……你想想看,这要是再加一种发送渠道,或者加一种消息类型,那得改多少代码?
桥接模式就是用来处理这种场景的。它的核心思想很简单:把抽象部分和实现部分拆开,用组合关系代替继承关系。
桥接模式的结构
桥接模式的结构其实不复杂,就四个角色:
- Abstraction(抽象类):定义抽象接口,持有 Implementor 的引用。
- RefinedAbstraction(扩展抽象类):对 Abstraction 进行扩展,可以增加新的行为。
- Implementor(实现类接口):定义实现类的接口,通常比 Abstraction 的接口更底层。
- ConcreteImplementor(具体实现类):实现 Implementor 接口,提供具体操作。
说白了,就是抽象层和实现层各自维护一个接口,然后通过组合把它们桥接起来。抽象层可以独立变化,实现层也可以独立变化,互不干扰。
核心要点:桥接模式的关键在于「组合优于继承」。抽象类持有实现接口的引用,而不是通过继承来绑定实现。
与继承的区别
很多人会问:用继承不行吗?我举个例子你就明白了。
假设我们有「形状」和「颜色」两个维度。用继承的话:
// 继承方式——类爆炸
class RedCircle extends Circle { ... }
class BlueCircle extends Circle { ... }
class RedSquare extends Square { ... }
class BlueSquare extends Square { ... }
// 再加一种颜色?再加一种形状?类数量 = 形状数 × 颜色数
用桥接模式呢?
// 桥接方式——线性增长
abstract class Shape {
protected Color color;
public Shape(Color color) { this.color = color; }
abstract void draw();
}
class Circle extends Shape {
public Circle(Color color) { super(color); }
void draw() { color.apply(); /* 画圆 */ }
}
class Square extends Shape {
public Square(Color color) { super(color); }
void draw() { color.apply(); /* 画方 */ }
}
interface Color { void apply(); }
class Red implements Color { public void apply() { /* 红色 */ } }
class Blue implements Color { public void apply() { /* 蓝色 */ } }
看到了吗?继承是 M × N 的类数量,桥接是 M + N。我当年在一个支付系统里就吃过这个亏,六个支付渠道、四种支付场景,用继承写了二十多个类,后来重构用桥接,代码量直接砍半。
避坑指南:我曾经以为桥接模式就是「接口+组合」,结果把抽象层和实现层的职责搞反了。记住:抽象层定义「做什么」,实现层定义「怎么做」。别混了。
桥接模式在 JDBC Driver 中的应用
说到桥接模式最经典的案例,JDBC Driver 绝对排第一。你想想看,Java 程序里操作数据库,不管你是 MySQL、Oracle 还是 PostgreSQL,代码几乎一模一样:
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
为什么能做到这一点?因为 JDBC 的设计者用了桥接模式。
- Abstraction(抽象层):
java.sql.Connection、java.sql.Statement、java.sql.ResultSet这些接口,定义了数据库操作的抽象。 - Implementor(实现层):各个数据库厂商提供的驱动实现,比如
com.mysql.cj.jdbc.ConnectionImpl、oracle.jdbc.driver.OracleConnection。 - 桥接点:
DriverManager负责加载驱动,并把抽象接口和具体实现桥接起来。
说白了,你写的代码只依赖 JDBC 的抽象接口,具体怎么跟数据库通信,那是驱动的事。换数据库?换个驱动 jar 包,改个连接字符串就行,代码一行都不用动。
个人经验:我建议你在设计自己的框架时,也可以借鉴 JDBC 的思路。把「接口规范」和「具体实现」彻底分离,这样后续扩展会轻松很多。我做过一个消息推送平台,就是照着这个思路设计的,后来接入新的推送渠道,只需要写一个实现类,完全不影响上层业务代码。
桥接模式的核心逻辑图
下面这张 SVG 图展示了桥接模式的核心结构,以及 JDBC 中的对应关系:
什么时候用桥接模式?
我个人总结了三个场景,遇到其中一个就可以考虑桥接:
- 一个类存在两个独立变化的维度,比如形状和颜色、消息类型和发送渠道、日志级别和输出目标。
- 不希望抽象和实现之间有固定的绑定关系,运行时可以动态切换实现。
- 类的继承层次过深,导致类数量爆炸,维护成本飙升。
一句话总结:桥接模式就是「用组合代替继承,把多维度的变化拆开,让它们各自独立演化」。JDBC 能支持那么多数据库,靠的就是这个思路。
嗯,桥接模式就讲到这里。记住它的核心:抽象和实现解耦,各自独立变化。下次你遇到类爆炸的问题,不妨想想能不能用桥接来拆一拆。