桥接模式 (Bridge)
📖 通俗理解
想象你有一个遥控器和一台电视机:
- 遥控器可以有不同种类:普通遥控器、高级遥控器
- 电视机也可以有不同品牌:索尼、三星、小米
如果用继承来实现,你需要:
- 索尼普通遥控器、索尼高级遥控器
- 三星普通遥控器、三星高级遥控器
- 小米普通遥控器、小米高级遥控器...
类的数量会爆炸式增长!
桥接模式的解决方案:把遥控器和电视机分开,用一座"桥"连接它们。遥控器持有电视机的引用,需要什么品牌的电视就传入什么品牌。
🎯 解决什么问题?
问题:当一个类存在两个或多个独立变化的维度时,使用继承会导致类爆炸。
例子:
- 形状(圆形、方形)× 颜色(红色、蓝色)= 4个类
- 形状(圆形、方形、三角形)× 颜色(红、蓝、绿)= 9个类
- 随着维度增加,类的数量成倍增长!
解决:把这两个维度分离,用组合代替继承。
🌰 生活中的例子
- 遥控器和电视:遥控器可以控制不同品牌的电视
- 手机和充电器:不同手机可以用同一个充电器
- 画笔和颜色:同一支笔可以蘸不同的颜料
- 车和发动机:同一款车可以装不同品牌的发动机
💻 Java 代码实现
场景:不同形状 × 不同颜色
第一步:定义实现接口(颜色)
/**
* 实现者接口:颜色
*/
public interface Color {
String fill();
}
/**
* 具体实现:红色
*/
public class RedColor implements Color {
@Override
public String fill() {
return "红色";
}
}
/**
* 具体实现:蓝色
*/
public class BlueColor implements Color {
@Override
public String fill() {
return "蓝色";
}
}
/**
* 具体实现:绿色
*/
public class GreenColor implements Color {
@Override
public String fill() {
return "绿色";
}
}
第二步:定义抽象类(形状)
/**
* 抽象类:形状
* 持有颜色的引用(这就是"桥")
*/
public abstract class Shape {
// 桥接:持有实现者的引用
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
/**
* 扩展抽象:圆形
*/
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("画一个" + color.fill() + "的圆形 ●");
}
}
/**
* 扩展抽象:正方形
*/
public class Square extends Shape {
public Square(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("画一个" + color.fill() + "的正方形 ■");
}
}
/**
* 扩展抽象:三角形
*/
public class Triangle extends Shape {
public Triangle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("画一个" + color.fill() + "的三角形 ▲");
}
}
第三步:使用桥接
public class Client {
public static void main(String[] args) {
// 红色圆形
Shape redCircle = new Circle(new RedColor());
redCircle.draw();
// 蓝色正方形
Shape blueSquare = new Square(new BlueColor());
blueSquare.draw();
// 绿色三角形
Shape greenTriangle = new Triangle(new GreenColor());
greenTriangle.draw();
// 可以任意组合!
System.out.println("--- 动态切换颜色 ---");
Shape circle = new Circle(new BlueColor());
circle.draw(); // 蓝色圆形
}
}
输出:
画一个红色的圆形 ●
画一个蓝色的正方形 ■
画一个绿色的三角形 ▲
--- 动态切换颜色 ---
画一个蓝色的圆形 ●
🔥 实战案例:消息发送系统
不同类型的消息(普通、加急、特急)× 不同发送方式(短信、邮件、微信)
/**
* 实现接口:消息发送方式
*/
public interface MessageSender {
void send(String message, String to);
}
/**
* 具体实现:短信发送
*/
public class SmsSender implements MessageSender {
@Override
public void send(String message, String to) {
System.out.println("【短信】发送给 " + to + ": " + message);
}
}
/**
* 具体实现:邮件发送
*/
public class EmailSender implements MessageSender {
@Override
public void send(String message, String to) {
System.out.println("【邮件】发送给 " + to + ": " + message);
}
}
/**
* 具体实现:微信发送
*/
public class WechatSender implements MessageSender {
@Override
public void send(String message, String to) {
System.out.println("【微信】发送给 " + to + ": " + message);
}
}
/**
* 抽象类:消息
*/
public abstract class Message {
// 桥接
protected MessageSender sender;
public Message(MessageSender sender) {
this.sender = sender;
}
public abstract void sendMessage(String content, String to);
}
/**
* 普通消息
*/
public class NormalMessage extends Message {
public NormalMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content, String to) {
String message = "【普通】" + content;
sender.send(message, to);
}
}
/**
* 加急消息
*/
public class UrgentMessage extends Message {
public UrgentMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content, String to) {
String message = "【加急!】" + content;
sender.send(message, to);
}
}
/**
* 特急消息
*/
public class VeryUrgentMessage extends Message {
public VeryUrgentMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content, String to) {
String message = "【特急!!!】" + content + " 请立即处理!";
// 特急消息:多渠道同时发送
sender.send(message, to);
System.out.println(" → 同时电话通知...");
}
}
使用方式:
public class Client {
public static void main(String[] args) {
// 普通消息 + 短信
Message msg1 = new NormalMessage(new SmsSender());
msg1.sendMessage("下午3点开会", "张三");
System.out.println();
// 加急消息 + 邮件
Message msg2 = new UrgentMessage(new EmailSender());
msg2.sendMessage("项目明天上线,请准备", "开发团队");
System.out.println();
// 特急消息 + 微信
Message msg3 = new VeryUrgentMessage(new WechatSender());
msg3.sendMessage("生产环境故障", "运维团队");
}
}
输出:
【短信】发送给 张三: 【普通】下午3点开会
【邮件】发送给 开发团队: 【加急!】项目明天上线,请准备
【微信】发送给 运维团队: 【特急!!!】生产环境故障 请立即处理!
→ 同时电话通知...
🔥 实战案例2:JDBC 驱动
JDBC 就是桥接模式的典型应用!
/**
* 模拟 JDBC 的桥接结构
*/
// 实现接口:数据库驱动
public interface Driver {
Connection connect(String url);
}
// MySQL 驱动
public class MySQLDriver implements Driver {
@Override
public Connection connect(String url) {
System.out.println("使用 MySQL 驱动连接: " + url);
return new MySQLConnection();
}
}
// Oracle 驱动
public class OracleDriver implements Driver {
@Override
public Connection connect(String url) {
System.out.println("使用 Oracle 驱动连接: " + url);
return new OracleConnection();
}
}
// 抽象类:DriverManager(桥)
public class DriverManager {
private Driver driver;
public void setDriver(Driver driver) {
this.driver = driver;
}
public Connection getConnection(String url) {
return driver.connect(url);
}
}
📊 类图结构
┌─────────────────┐
│ Abstraction │ ──────────────┐
│ + operation() │ │
└────────▲────────┘ │ 桥
│ ▼
┌────────────┴────────────┐ ┌───────────────────┐
│ │ │ Implementor │
┌───┴─────────┐ ┌────────────┴┐ │ + operationImpl() │
│RefinedAbst1 │ │RefinedAbst2 │ └─────────▲─────────┘
└─────────────┘ └─────────────┘ │
┌──────────┴──────────┐
│ │
┌──────┴──────┐ ┌───────┴─────┐
│ConcreteImpl1│ │ConcreteImpl2│
└─────────────┘ └─────────────┘
⚠️ 桥接模式 vs 策略模式
很多人会搞混这两个模式,它们的区别是:
| 对比项 | 桥接模式 | 策略模式 |
|---|---|---|
| 目的 | 解耦抽象和实现 | 切换算法策略 |
| 关注点 | 结构设计 | 行为切换 |
| 变化维度 | 两个维度都可变 | 只有策略在变 |
| 使用场景 | 设计阶段 | 运行时切换算法 |
✅ 适用场景
- 多维度变化:一个类存在两个或多个独立变化的维度
- 避免继承爆炸:继承层次过深或子类过多
- 需要运行时切换实现:想在运行时动态改变实现
小结
桥接模式的核心:将抽象和实现分离,让它们可以独立变化。
关键:
- 抽象部分持有实现部分的引用(桥)
- 两个维度都可以独立扩展
- 用组合代替继承,避免类爆炸
👉 下一篇:组合模式
