欢迎你来读这篇博客,这篇博客主要是关于桥接模式。
其中包括桥接模式的核心思想、适用场景、优缺点、与适配器模式和策略模式的区别,以及 Java 后端开发中消息通知系统的完整案例。
序言
在软件开发中,有一种很典型的复杂度来源:
一个业务对象存在多个变化维度。
比如消息通知系统。
消息类型可能有:
发送渠道可能有:
如果直接用继承来建模,很容易写成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| EmailNormalMessage SmsNormalMessage WeChatNormalMessage DingTalkNormalMessage
EmailUrgentMessage SmsUrgentMessage WeChatUrgentMessage DingTalkUrgentMessage
EmailCodeMessage SmsCodeMessage WeChatCodeMessage DingTalkCodeMessage
EmailMarketingMessage SmsMarketingMessage WeChatMarketingMessage DingTalkMarketingMessage
|
光是 4 种消息类型和 4 种发送渠道,就有 16 个类。
如果再新增一种消息类型,所有渠道都要配一遍。
如果再新增一种发送渠道,所有消息类型也要配一遍。
这就是典型的类爆炸。
桥接模式就是为了解决这类问题:
当一个类存在多个独立变化维度时,不要把它们全部塞进继承层级,而是把抽象部分和实现部分拆开,让它们可以独立变化。
说得直白一点:
桥接模式就是把“是什么”和“怎么做”拆开,中间用组合关系搭一座桥。
正文
chapter 1:什么是桥接模式
桥接模式,英文是 Bridge Pattern,属于结构型设计模式。
它的定义是:
将抽象部分与实现部分分离,使它们都可以独立变化。
这里的“抽象部分”和“实现部分”不是简单指接口和实现类,而是指系统中的两个变化维度。
比如消息通知系统里:
- 抽象部分:消息类型,例如普通消息、加急消息、验证码消息;
- 实现部分:发送渠道,例如邮件、短信、企业微信、钉钉。
如果用继承强行组合这两个维度,就会类爆炸。
如果用桥接模式,就可以拆成两个独立层次:
1 2
| 消息类型维度:NormalMessage、UrgentMessage、VerifyCodeMessage 发送渠道维度:EmailSender、SmsSender、WeChatSender
|
消息类型内部持有发送渠道接口:
1
| protected MessageSender messageSender;
|
这样消息类型和发送渠道就通过组合连接起来。
这座“组合之桥”,就是桥接模式的核心。
chapter 2:为什么需要桥接模式
如果一个系统只有一个变化维度,继承通常还能接受。
比如:
1 2 3 4
| Message ├── NormalMessage ├── UrgentMessage └── VerifyCodeMessage
|
但如果出现第二个变化维度,就容易失控。
比如消息还要区分发送渠道:
1 2 3 4 5 6 7 8 9 10
| Message ├── EmailNormalMessage ├── SmsNormalMessage ├── WeChatNormalMessage ├── EmailUrgentMessage ├── SmsUrgentMessage ├── WeChatUrgentMessage ├── EmailVerifyCodeMessage ├── SmsVerifyCodeMessage └── WeChatVerifyCodeMessage
|
如果有 m 种消息类型,n 种发送渠道,类数量可能变成:
这不是设计模式,是排列组合开始上班。
桥接模式的思路是:
不要让两个维度在继承体系里相乘,而是让它们在运行时组合。
拆开之后,类数量变成:
例如:
1 2 3
| 消息类型:3 个类 发送渠道:3 个类 总共:6 个类
|
而不是 9 个组合类。
当维度更多时,差距会更明显。
chapter 3:桥接模式的核心结构
桥接模式通常包含四个角色:
- Abstraction 抽象类:定义抽象部分的接口,并持有实现部分接口。
- RefinedAbstraction 扩展抽象类:扩展抽象部分的具体行为。
- Implementor 实现接口:定义实现部分的接口。
- ConcreteImplementor 具体实现类:实现具体实现部分。
结构如下:
1 2 3 4 5 6 7
| Client │ ▼ Abstraction ───────► Implementor ▲ ▲ │ │ RefinedAbstraction ConcreteImplementor
|
在消息通知案例中:
1 2 3 4 5
| Abstraction:Message RefinedAbstraction:NormalMessage、UrgentMessage、VerifyCodeMessage
Implementor:MessageSender ConcreteImplementor:EmailSender、SmsSender、WeChatSender
|
Message 不直接负责发送细节,而是把发送动作委托给 MessageSender。
这就是“抽象”和“实现”的分离。
chapter 4:桥接模式解决的是多维度变化
桥接模式最关键的识别点是:
系统中存在两个或多个可以独立变化的维度。
例如:
1. 消息类型 × 发送渠道
- 普通消息、加急消息、验证码消息;
- 邮件、短信、微信、钉钉。
2. 文件类型 × 存储方式
- 图片、视频、文档;
- 本地存储、OSS、COS、MinIO。
3. 支付业务 × 支付渠道
- 普通支付、分账支付、退款、预授权;
- 支付宝、微信、Stripe、PayPal。
4. 报表类型 × 导出格式
- 订单报表、支付报表、用户报表;
- Excel、CSV、PDF、JSON。
5. 日志类型 × 输出目标
- 业务日志、审计日志、监控日志;
- 控制台、文件、Kafka、ELK。
如果这些维度被写进同一个继承体系,类会迅速膨胀。
桥接模式的目标就是拆维度。
chapter 5:不用桥接模式会怎样
假设我们做一个消息通知系统。
最初只有邮件通知。
1 2 3 4
| public abstract class Message {
public abstract void send(String receiver, String content); }
|
普通邮件消息:
1 2 3 4 5 6 7
| public class EmailNormalMessage extends Message {
@Override public void send(String receiver, String content) { System.out.println("发送普通邮件给 " + receiver + ",内容:" + content); } }
|
加急邮件消息:
1 2 3 4 5 6 7
| public class EmailUrgentMessage extends Message {
@Override public void send(String receiver, String content) { System.out.println("发送加急邮件给 " + receiver + ",内容:[加急]" + content); } }
|
后来新增短信渠道。
1 2 3 4 5 6 7
| public class SmsNormalMessage extends Message {
@Override public void send(String receiver, String content) { System.out.println("发送普通短信给 " + receiver + ",内容:" + content); } }
|
1 2 3 4 5 6 7
| public class SmsUrgentMessage extends Message {
@Override public void send(String receiver, String content) { System.out.println("发送加急短信给 " + receiver + ",内容:[加急]" + content); } }
|
再后来新增企业微信。
1 2
| public class WeChatNormalMessage extends Message { }
|
1 2
| public class WeChatUrgentMessage extends Message { }
|
这时问题已经出现了。
如果有 4 种消息类型和 5 种发送渠道,就要 20 个类。
如果未来还有消息模板、国际化、重试策略、限流策略,那就更乱。
这说明一个问题:
当前继承体系把两个独立变化维度绑死了。
桥接模式要做的,就是把它们拆开。
chapter 6:案例背景:消息通知系统
现在我们重新设计消息通知系统。
需求如下:
- 支持多种消息类型;
- 支持多种发送渠道;
- 消息类型和发送渠道可以自由组合;
- 新增消息类型时,不修改发送渠道;
- 新增发送渠道时,不修改消息类型;
- 业务层尽量依赖抽象接口。
消息类型包括:
发送渠道包括:
使用桥接模式后,我们把系统拆成两个维度:
1 2
| 消息抽象维度:Message 发送实现维度:MessageSender
|
Message 持有 MessageSender。
1 2 3 4 5 6 7 8 9 10
| public abstract class Message {
protected final MessageSender messageSender;
protected Message(MessageSender messageSender) { this.messageSender = messageSender; }
public abstract void send(String receiver, String content); }
|
chapter 7:定义实现部分接口:MessageSender
先定义发送渠道接口。
1 2 3 4
| public interface MessageSender {
void send(String receiver, String content); }
|
这个接口代表桥接模式中的 Implementor。
它只关心一件事:
如何把消息发出去。
至于这条消息是普通消息、加急消息还是验证码消息,它不关心。
chapter 8:定义具体发送渠道
1. 邮件发送器
1 2 3 4 5 6 7
| public class EmailMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过邮件发送给 " + receiver + ",内容:" + content); } }
|
2. 短信发送器
1 2 3 4 5 6 7
| public class SmsMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过短信发送给 " + receiver + ",内容:" + content); } }
|
3. 企业微信发送器
1 2 3 4 5 6 7
| public class WeChatMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过企业微信发送给 " + receiver + ",内容:" + content); } }
|
这几个类是桥接模式中的具体实现类。
它们只处理发送方式的差异。
chapter 9:定义抽象部分:Message
然后定义消息抽象类。
1 2 3 4 5 6 7 8 9 10
| public abstract class Message {
protected final MessageSender messageSender;
protected Message(MessageSender messageSender) { this.messageSender = messageSender; }
public abstract void send(String receiver, String content); }
|
这里的关键点是:
1
| protected final MessageSender messageSender;
|
消息类不直接依赖邮件、短信、企业微信这些具体发送器,而是依赖发送接口。
消息类型通过组合持有发送实现。
这就是桥接模式里的“桥”。
chapter 10:定义具体消息类型
1. 普通消息
1 2 3 4 5 6 7 8 9 10 11
| public class NormalMessage extends Message {
public NormalMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { messageSender.send(receiver, content); } }
|
普通消息不做特殊处理,直接发送。
2. 加急消息
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class UrgentMessage extends Message {
public UrgentMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { String urgentContent = "[加急] " + content;
messageSender.send(receiver, urgentContent); } }
|
加急消息在内容前面加上 [加急]。
以后还可以在这里加:
- 优先级;
- 重试;
- 告警升级;
- 多渠道补偿;
- 发送记录标记。
3. 验证码消息
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class VerifyCodeMessage extends Message {
public VerifyCodeMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { String codeContent = "您的验证码是:" + content + ",5 分钟内有效。";
messageSender.send(receiver, codeContent); } }
|
验证码消息负责处理验证码内容格式。
发送渠道仍然由 MessageSender 决定。
chapter 11:客户端使用
现在可以自由组合消息类型和发送渠道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Client {
public static void main(String[] args) { MessageSender emailSender = new EmailMessageSender(); MessageSender smsSender = new SmsMessageSender(); MessageSender weChatSender = new WeChatMessageSender();
Message normalEmailMessage = new NormalMessage(emailSender); normalEmailMessage.send("user@example.com", "欢迎注册系统");
Message urgentSmsMessage = new UrgentMessage(smsSender); urgentSmsMessage.send("13800000000", "您的订单支付异常,请及时处理");
Message verifyCodeWeChatMessage = new VerifyCodeMessage(weChatSender); verifyCodeWeChatMessage.send("zhangsan", "9527"); } }
|
输出类似:
1 2 3
| 通过邮件发送给 user@example.com,内容:欢迎注册系统 通过短信发送给 13800000000,内容:[加急] 您的订单支付异常,请及时处理 通过企业微信发送给 zhangsan,内容:您的验证码是:9527,5 分钟内有效。
|
这个结构下:
- 新增消息类型,只需要新增
Message 子类;
- 新增发送渠道,只需要新增
MessageSender 实现类;
- 二者可以自由组合。
这就是桥接模式的价值。
chapter 12:新增一种消息类型
假设现在新增营销消息。
营销消息需要自动添加退订提示。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MarketingMessage extends Message {
public MarketingMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { String marketingContent = content + ",回复 TD 退订。";
messageSender.send(receiver, marketingContent); } }
|
它可以直接搭配已有发送渠道:
1 2 3 4 5
| MessageSender smsSender = new SmsMessageSender();
Message marketingMessage = new MarketingMessage(smsSender);
marketingMessage.send("13800000000", "会员日优惠活动开始了");
|
不需要修改:
EmailMessageSender
SmsMessageSender
WeChatMessageSender
chapter 13:新增一种发送渠道
假设现在新增钉钉发送渠道。
1 2 3 4 5 6 7
| public class DingTalkMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过钉钉发送给 " + receiver + ",内容:" + content); } }
|
它可以直接搭配已有消息类型:
1 2 3 4 5
| MessageSender dingTalkSender = new DingTalkMessageSender();
Message urgentMessage = new UrgentMessage(dingTalkSender);
urgentMessage.send("team-group", "线上订单支付失败率过高");
|
不需要修改:
NormalMessage
UrgentMessage
VerifyCodeMessage
MarketingMessage
这就是“抽象部分”和“实现部分”独立扩展。
chapter 14:在 Spring Boot 中落地
在 Spring Boot 项目中,发送渠道通常会注册成 Bean。
先定义发送接口:
1 2 3 4 5 6
| public interface MessageSender {
String channel();
void send(String receiver, String content); }
|
邮件发送器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.stereotype.Component;
@Component public class EmailMessageSender implements MessageSender {
@Override public String channel() { return "EMAIL"; }
@Override public void send(String receiver, String content) { System.out.println("通过邮件发送给 " + receiver + ",内容:" + content); } }
|
短信发送器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.stereotype.Component;
@Component public class SmsMessageSender implements MessageSender {
@Override public String channel() { return "SMS"; }
@Override public void send(String receiver, String content) { System.out.println("通过短信发送给 " + receiver + ",内容:" + content); } }
|
企业微信发送器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.stereotype.Component;
@Component public class WeChatMessageSender implements MessageSender {
@Override public String channel() { return "WECHAT"; }
@Override public void send(String receiver, String content) { System.out.println("通过企业微信发送给 " + receiver + ",内容:" + content); } }
|
然后定义发送器路由器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import org.springframework.stereotype.Component;
import java.util.List; import java.util.Map; import java.util.stream.Collectors;
@Component public class MessageSenderRouter {
private final Map<String, MessageSender> senderMap;
public MessageSenderRouter(List<MessageSender> senders) { this.senderMap = senders.stream() .collect(Collectors.toUnmodifiableMap(MessageSender::channel, sender -> sender)); }
public MessageSender getSender(String channel) { MessageSender sender = senderMap.get(channel);
if (sender == null) { throw new IllegalArgumentException("Unsupported message channel: " + channel); }
return sender; } }
|
消息类型可以继续使用普通 Java 类:
1 2 3 4 5 6 7 8 9 10
| public abstract class Message {
protected final MessageSender messageSender;
protected Message(MessageSender messageSender) { this.messageSender = messageSender; }
public abstract void send(String receiver, String content); }
|
消息工厂根据消息类型创建不同消息对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import org.springframework.stereotype.Component;
@Component public class MessageFactory {
private final MessageSenderRouter messageSenderRouter;
public MessageFactory(MessageSenderRouter messageSenderRouter) { this.messageSenderRouter = messageSenderRouter; }
public Message createMessage(String messageType, String channel) { MessageSender sender = messageSenderRouter.getSender(channel);
if ("NORMAL".equals(messageType)) { return new NormalMessage(sender); }
if ("URGENT".equals(messageType)) { return new UrgentMessage(sender); }
if ("VERIFY_CODE".equals(messageType)) { return new VerifyCodeMessage(sender); }
throw new IllegalArgumentException("Unsupported message type: " + messageType); } }
|
业务服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.springframework.stereotype.Service;
@Service public class NotificationService {
private final MessageFactory messageFactory;
public NotificationService(MessageFactory messageFactory) { this.messageFactory = messageFactory; }
public void send(String messageType, String channel, String receiver, String content) { Message message = messageFactory.createMessage(messageType, channel);
message.send(receiver, content); } }
|
调用方式:
1 2 3
| notificationService.send("URGENT", "SMS", "13800000000", "订单支付异常,请及时处理"); notificationService.send("VERIFY_CODE", "EMAIL", "user@example.com", "9527"); notificationService.send("NORMAL", "WECHAT", "zhangsan", "欢迎使用系统");
|
这个 Spring Boot 版本结合了:
- 桥接模式;
- 工厂模式;
- Spring Bean 自动注入;
- 路由表;
- 面向接口编程。
真实项目里经常不是单一模式,而是多个模式组合使用。
模式不是单打独斗的武林高手,更像一套组合拳。
chapter 15:继续优化:消息类型也交给 Spring 管理
上面的 MessageFactory 里还有 if else。
如果消息类型很多,可以继续抽象。
定义消息处理器接口:
1 2 3 4 5 6
| public interface MessageTemplate {
String type();
String format(String content); }
|
普通消息模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.stereotype.Component;
@Component public class NormalMessageTemplate implements MessageTemplate {
@Override public String type() { return "NORMAL"; }
@Override public String format(String content) { return content; } }
|
加急消息模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.stereotype.Component;
@Component public class UrgentMessageTemplate implements MessageTemplate {
@Override public String type() { return "URGENT"; }
@Override public String format(String content) { return "[加急] " + content; } }
|
验证码消息模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import org.springframework.stereotype.Component;
@Component public class VerifyCodeMessageTemplate implements MessageTemplate {
@Override public String type() { return "VERIFY_CODE"; }
@Override public String format(String content) { return "您的验证码是:" + content + ",5 分钟内有效。"; } }
|
然后定义消息模板路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import org.springframework.stereotype.Component;
import java.util.List; import java.util.Map; import java.util.stream.Collectors;
@Component public class MessageTemplateRouter {
private final Map<String, MessageTemplate> templateMap;
public MessageTemplateRouter(List<MessageTemplate> templates) { this.templateMap = templates.stream() .collect(Collectors.toUnmodifiableMap(MessageTemplate::type, template -> template)); }
public MessageTemplate getTemplate(String type) { MessageTemplate template = templateMap.get(type);
if (template == null) { throw new IllegalArgumentException("Unsupported message type: " + type); }
return template; } }
|
业务服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import org.springframework.stereotype.Service;
@Service public class NotificationService {
private final MessageTemplateRouter messageTemplateRouter;
private final MessageSenderRouter messageSenderRouter;
public NotificationService(MessageTemplateRouter messageTemplateRouter, MessageSenderRouter messageSenderRouter) { this.messageTemplateRouter = messageTemplateRouter; this.messageSenderRouter = messageSenderRouter; }
public void send(String messageType, String channel, String receiver, String content) { MessageTemplate template = messageTemplateRouter.getTemplate(messageType); MessageSender sender = messageSenderRouter.getSender(channel);
String formattedContent = template.format(content);
sender.send(receiver, formattedContent); } }
|
这个版本其实更像“桥接模式 + 策略模式”。
两个维度都被拆开了:
- 消息类型维度:
MessageTemplate
- 发送渠道维度:
MessageSender
业务服务负责把两个维度组合起来。
这种写法在 Spring 项目中非常实用。
chapter 16:桥接模式和适配器模式的区别
桥接模式和适配器模式都属于结构型模式,而且都使用组合,但它们的目的完全不同。
| 对比项 |
桥接模式 |
适配器模式 |
| 使用时机 |
设计初期主动拆分维度 |
已有接口不兼容时进行适配 |
| 目的 |
让抽象和实现独立变化 |
让不兼容接口能协同工作 |
| 关注点 |
多维度扩展 |
接口转换 |
| 是否改变接口 |
不一定 |
通常会转换接口 |
| 典型场景 |
消息类型 × 发送渠道 |
第三方 SDK 接入 |
| 设计性质 |
事前设计 |
事后补救 |
一句话区分:
桥接模式是提前拆分变化维度。
适配器模式是后来解决接口不兼容。
例如:
1
| Message message = new UrgentMessage(new SmsMessageSender());
|
这是桥接模式。
1
| PaymentClient client = new AliPayClientAdapter(new AliPaySdkClient());
|
这是适配器模式。
chapter 17:桥接模式和策略模式的区别
桥接模式和策略模式也很容易混。
它们都会用接口和组合。
但两者侧重点不同。
| 对比项 |
桥接模式 |
策略模式 |
| 关注点 |
拆分两个独立变化维度 |
替换一组算法或行为 |
| 结构 |
抽象层持有实现层 |
上下文持有策略接口 |
| 目的 |
避免类爆炸 |
让算法可替换 |
| 典型数量 |
通常有两个维度 |
通常一个行为维度 |
| 示例 |
消息类型 × 发送渠道 |
不同优惠计算策略 |
策略模式解决的是:
同一个行为有多种实现,运行时选择一种。
桥接模式解决的是:
一个对象存在多个变化维度,需要拆开组合。
在 Spring 项目中,桥接模式和策略模式经常混合出现。
比如前面的最终版本:
1 2
| MessageTemplate template = messageTemplateRouter.getTemplate(messageType); MessageSender sender = messageSenderRouter.getSender(channel);
|
这里 MessageTemplate 和 MessageSender 都可以看作策略,但整个结构体现的是桥接思想:两个维度独立变化。
chapter 18:桥接模式和装饰器模式的区别
装饰器模式也是通过组合扩展功能,但和桥接模式不同。
| 对比项 |
桥接模式 |
装饰器模式 |
| 目的 |
拆分维度,避免类爆炸 |
动态增强对象功能 |
| 结构 |
抽象层组合实现层 |
装饰器和被装饰者实现同一接口 |
| 关注点 |
横向组合多个维度 |
纵向叠加多个功能 |
| 示例 |
消息类型 + 发送渠道 |
给发送器增加日志、限流、重试 |
桥接模式像把两个维度拆开,再横向组合。
装饰器模式像一层一层往对象外面套能力。
例如:
1
| MessageSender sender = new SmsMessageSender();
|
桥接是:
1
| Message message = new UrgentMessage(sender);
|
装饰器可能是:
1 2 3 4 5
| MessageSender sender = new RetryMessageSenderDecorator( new LogMessageSenderDecorator( new SmsMessageSender() ) );
|
一个是“维度组合”,一个是“功能叠加”。
chapter 19:桥接模式和继承的关系
桥接模式常常被理解为:
用组合替代继承。
但更准确地说,它是:
把继承体系中多个混在一起的变化维度拆开,让它们通过组合协作。
继承不是不能用。
问题是当继承层级同时表达多个维度时,就会开始膨胀。
例如:
1 2 3 4 5
| Message ├── EmailNormalMessage ├── SmsNormalMessage ├── EmailUrgentMessage └── SmsUrgentMessage
|
这里一个类名里同时包含两个维度:
- Email / Sms:发送渠道;
- Normal / Urgent:消息类型。
这就是一个明显信号:
这个继承体系可能需要桥接模式。
桥接之后:
1 2 3 4 5 6 7
| Message ├── NormalMessage └── UrgentMessage
MessageSender ├── EmailMessageSender └── SmsMessageSender
|
组合使用:
1
| new UrgentMessage(new SmsMessageSender());
|
类名不再承载多个维度,结构更清晰。
chapter 20:桥接模式的优点
1. 避免类爆炸
把多个变化维度拆开,避免产生大量组合类。
2. 符合开闭原则
新增抽象类型或实现类型时,尽量不修改已有代码。
3. 提高扩展性
抽象部分和实现部分可以独立扩展。
4. 降低耦合
抽象部分不依赖具体实现类,只依赖实现接口。
5. 更适合多维度业务建模
例如类型 × 渠道、业务 × 平台、格式 × 存储等组合场景。
chapter 21:桥接模式的缺点
1. 增加理解成本
桥接模式会引入更多抽象层。
对于简单场景,可能会显得复杂。
2. 需要正确识别变化维度
如果变化维度识别错了,桥接模式反而会让设计变绕。
3. 可能过度设计
如果系统中只有一个维度变化,或者组合关系很少,就没必要使用桥接模式。
4. 调试链路可能变长
抽象层通过实现层完成工作,调用关系比直接继承更绕一点。
但在复杂系统里,这个代价通常是值得的。
chapter 22:适用场景
桥接模式适合以下场景。
1. 存在两个或多个独立变化维度
例如:
- 消息类型 × 发送渠道;
- 报表类型 × 导出格式;
- 文件类型 × 存储平台;
- 支付业务 × 支付渠道。
2. 使用继承会导致类爆炸
如果类名开始出现多个维度组合,比如:
1 2 3 4
| SmsUrgentMessage EmailUrgentMessage SmsNormalMessage EmailNormalMessage
|
就应该警惕。
3. 希望抽象和实现可以独立扩展
例如新增一个消息类型,不希望改发送渠道。
新增一个发送渠道,也不希望改消息类型。
4. 不希望业务代码依赖具体实现
通过接口和组合解耦具体实现。
5. 平台差异和业务类型差异都比较明显
例如多云存储、多支付平台、多消息渠道。
chapter 23:不适合使用的场景
以下场景不建议使用桥接模式。
1. 只有一个变化维度
如果只有消息类型变化,没有发送渠道变化,普通继承或者策略模式就够了。
2. 类数量很少且不会增长
如果只有两个类,长期不会扩展,就没必要提前桥接。
3. 维度之间不是独立变化
如果两个维度强绑定,不能自由组合,桥接模式意义不大。
比如某种消息类型只能通过某个固定渠道发送,组合空间很小,强行桥接可能增加复杂度。
4. 团队理解成本过高
桥接模式比适配器、策略、工厂更抽象。
如果团队对多维度设计不熟,建议先用更直观的接口和组合方式逐步演进。
chapter 24:真实项目中的实践建议
1. 看到类名里有多个维度,要警惕
例如:
1 2 3 4
| AliPayRefundService WxPayRefundService AliPayPayService WxPayPayService
|
这里可能有两个维度:
- 支付渠道:AliPay、WxPay;
- 业务动作:Pay、Refund。
可以考虑拆成:
1 2
| PaymentOperation PaymentChannel
|
2. 不要一开始就过度桥接
如果业务还不稳定,先保持简单。
等变化维度真的清晰后,再抽象也不迟。
桥接模式适合解决已经看得见的维度复杂度,而不是想象中的未来复杂度。
3. 桥接模式经常和工厂模式组合
桥接模式解决结构问题。
工厂模式负责根据配置创建组合对象。
例如:
1
| Message message = messageFactory.createMessage(messageType, channel);
|
4. Spring 项目可以用 Map 路由降低 if else
例如:
1 2
| Map<String, MessageSender> Map<String, MessageTemplate>
|
这样新增实现类时,只需要新增 Bean。
5. 注意抽象接口不要设计得太厚
MessageSender 最好只关心发送。
不要塞入太多业务方法。
例如不要让发送器同时负责:
- 订单状态判断;
- 用户权限校验;
- 营销规则计算;
- 账单生成;
- 库存处理。
否则它就不再是桥接实现层,而是业务黑洞。
6. 桥接不是为了消灭所有继承
桥接模式不是反继承。
它只是避免用继承表达多个独立变化维度。
单个维度内部仍然可以有继承或接口层次。
chapter 25:完整案例代码汇总
发送渠道接口
1 2 3 4
| public interface MessageSender {
void send(String receiver, String content); }
|
具体发送渠道
1 2 3 4 5 6 7
| public class EmailMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过邮件发送给 " + receiver + ",内容:" + content); } }
|
1 2 3 4 5 6 7
| public class SmsMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过短信发送给 " + receiver + ",内容:" + content); } }
|
1 2 3 4 5 6 7
| public class WeChatMessageSender implements MessageSender {
@Override public void send(String receiver, String content) { System.out.println("通过企业微信发送给 " + receiver + ",内容:" + content); } }
|
消息抽象类
1 2 3 4 5 6 7 8 9 10
| public abstract class Message {
protected final MessageSender messageSender;
protected Message(MessageSender messageSender) { this.messageSender = messageSender; }
public abstract void send(String receiver, String content); }
|
具体消息类型
1 2 3 4 5 6 7 8 9 10 11
| public class NormalMessage extends Message {
public NormalMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { messageSender.send(receiver, content); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class UrgentMessage extends Message {
public UrgentMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { String urgentContent = "[加急] " + content;
messageSender.send(receiver, urgentContent); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class VerifyCodeMessage extends Message {
public VerifyCodeMessage(MessageSender messageSender) { super(messageSender); }
@Override public void send(String receiver, String content) { String codeContent = "您的验证码是:" + content + ",5 分钟内有效。";
messageSender.send(receiver, codeContent); } }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Client {
public static void main(String[] args) { MessageSender emailSender = new EmailMessageSender(); MessageSender smsSender = new SmsMessageSender(); MessageSender weChatSender = new WeChatMessageSender();
Message normalEmailMessage = new NormalMessage(emailSender); normalEmailMessage.send("user@example.com", "欢迎注册系统");
Message urgentSmsMessage = new UrgentMessage(smsSender); urgentSmsMessage.send("13800000000", "您的订单支付异常,请及时处理");
Message verifyCodeWeChatMessage = new VerifyCodeMessage(weChatSender); verifyCodeWeChatMessage.send("zhangsan", "9527"); } }
|
chapter 26:一句话总结
桥接模式的本质是:
将抽象部分和实现部分拆开,用组合关系建立桥梁,让两个变化维度可以独立扩展。
它最适合解决“多维度变化导致类爆炸”的问题。
在 Java 后端中,常见场景包括:
- 消息类型 × 发送渠道;
- 文件类型 × 存储平台;
- 支付业务 × 支付渠道;
- 报表类型 × 导出格式;
- 日志类型 × 输出目标。
如果一个类名里开始同时出现两个业务维度,就要想想:是不是该搭一座桥了。
桥接模式不是为了让代码显得抽象,而是为了让变化不会互相拖累。
好的桥接设计,像一座真正的桥:两边都能独立发展,中间还能稳定通行。
参考资料
- Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software.
- Robert C. Martin. Agile Software Development, Principles, Patterns, and Practices.
- Joshua Bloch. Effective Java.
- Spring Framework Documentation: Core Technologies - The IoC Container.
- Refactoring Guru: Bridge Pattern.
- SourceMaking: Bridge Design Pattern.
启示录
富贵岂由人,时会高志须酬。
能成功于千载者,必以近察远。