桥接模式:定义、结构、实现、优缺点、应用场景
桥接模式,说实话,是我个人非常喜欢的一个设计模式。为什么?因为它解决了一个很实际的问题——把抽象和实现解耦,让它们可以独立变化。你想想看,在软件开发中,我们经常遇到这样的场景:一个类有两个维度的变化,比如形状和颜色,或者消息类型和发送方式。如果直接用继承,类会爆炸式增长。桥接模式就是来救场的。
什么是桥接模式?
桥接模式(Bridge Pattern)的核心思想很简单:将抽象部分与它的实现部分分离,使它们都可以独立地变化。它通过组合关系代替继承关系,从而降低了抽象和实现这两个可变维度的耦合度。
我记得有一次,我在做一个跨平台UI框架。我们需要支持Windows、macOS、Linux三种平台,同时还要支持按钮、文本框、下拉框等多种控件。如果每个控件都针对每个平台写一个子类,那得多少个类?3×3=9个?不对,实际上控件种类远不止3种,平台也可能增加。我当时就用了桥接模式,把控件抽象和平台实现分开,后来新增一个控件或者新增一个平台,都只需要加一个类,而不是乘数级的类。
模式结构
桥接模式的结构其实不复杂,我画个图你就明白了。
结构中的角色,我简单说一下:
- Abstraction(抽象类):定义抽象接口,持有Implementor的引用。说白了,就是「我要做什么」。
- RefinedAbstraction(扩展抽象类):对抽象类进行扩展,可以增加新的行为。
- Implementor(实现接口):定义实现类的接口,通常比抽象接口更底层。这就是「具体怎么做」。
- ConcreteImplementor(具体实现类):实现Implementor接口,给出具体操作。
代码实现
咱们用个实际例子来演示。假设我们要开发一个消息通知系统,消息可以分为普通消息和紧急消息,发送方式可以是邮件发送和短信发送。这就是两个维度:消息类型和发送方式。
// 1. 实现接口 - 消息发送方式
interface MessageSender {
void send(String message);
}
// 具体实现类 - 邮件发送
class EmailSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送邮件: " + message);
}
}
// 具体实现类 - 短信发送
class SmsSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送短信: " + message);
}
}
// 2. 抽象类 - 消息类型
abstract class Message {
protected MessageSender sender;
public Message(MessageSender sender) {
this.sender = sender;
}
public abstract void sendMessage(String content);
}
// 扩展抽象类 - 普通消息
class NormalMessage extends Message {
public NormalMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content) {
System.out.println("[普通消息]");
sender.send(content);
}
}
// 扩展抽象类 - 紧急消息
class UrgentMessage extends Message {
public UrgentMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content) {
System.out.println("[紧急消息] 请立即处理!");
sender.send(content);
}
}
// 3. 客户端使用
public class Client {
public static void main(String[] args) {
// 普通消息 + 邮件发送
Message normalEmail = new NormalMessage(new EmailSender());
normalEmail.sendMessage("今天下午开会");
// 紧急消息 + 短信发送
Message urgentSms = new UrgentMessage(new SmsSender());
urgentSms.sendMessage("服务器宕机了!");
}
}
你看,这样设计之后,如果以后要增加「微信发送」方式,只需要加一个 WechatSender 类,实现 MessageSender 接口就行。如果要增加「机密消息」类型,也只需要扩展 Message 类。两个维度互不影响。
优缺点分析
| 优点 | 缺点 |
|---|---|
|
|
应用场景
桥接模式在哪些地方用得比较多?我结合自己的项目经验,给你列几个典型场景:
- 跨平台UI框架:控件抽象(按钮、文本框)与平台实现(Windows、macOS)分离。Qt、Java AWT/Swing 都用了这个思路。
- 多种数据库驱动:JDBC 就是典型的桥接模式。应用程序通过 JDBC 接口操作数据库,具体实现由各数据库厂商提供。
- 消息通知系统:就像我们上面的例子,消息类型和发送方式是两个独立维度。
- 图形绘制系统:形状(圆形、矩形)和绘制方式(矢量图、位图)可以独立变化。
- 日志记录系统:日志级别(INFO、ERROR)和输出目标(文件、控制台、远程服务器)分离。
嗯,桥接模式就讲到这里。它不像单例模式那么常用,但在合适的场景下,它能让你的代码结构清晰很多。我个人建议,在系统设计初期就考虑好哪些维度可能会变化,提前用桥接模式做好隔离,比后期重构要省力得多。