写在前面
Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!
一、背景与初始实现
1.1 业务需求背景
电商物流系统需要支持:
- 消息类型:普通消息、紧急消息
- 通知渠道:极光推送、短信通知
- 扩展预期:未来可能新增微信通知、邮件通知等渠道
1.2 初始实现方案(简单工厂方法模式)
普通消息类
typescript
public class NormalMessage {
public void sendByJPush(String content) {
System.out.println("通过【极光】推送: " + content + " [普通]");
}
public void sendBySMS(String content) {
System.out.println("通过【短信】发送: " + content + " [普通]");
}
}
紧急消息类
typescript
public class EmergencyMessage {
public void sendByJPush(String content) {
System.out.println("通过【极光】推送: " + content + " [紧急]");
}
public void sendBySMS(String content) {
System.out.println("通过【短信】发送: " + content + " [紧急]");
}
}
客户端调用
ini
public class MessageClient {
private final NormalMessage normalMessage = new NormalMessage();
private final EmergencyMessage emergencyMessage = new EmergencyMessage();
public void send(Integer type, Integer channel, String content) {
if (type == 1) {
if (channel == 1) normalMessage.sendByJPush(content);
else if (channel == 2) normalMessage.sendBySMS(content);
} else if (type == 2) {
if (channel == 1) emergencyMessage.sendByJPush(content);
else if (channel == 2) emergencyMessage.sendBySMS(content);
}
}
}
二、暴露的问题
2.1 新增微信渠道时的问题
typescript
// 需要修改所有消息类
public class NormalMessage {
// 新增微信发送方法
public void sendByWeChat(String content) {
System.out.println("通过【微信】发送: " + content + " [普通]");
}
}
// 客户端需要增加分支判断
if (channel == 3) {
message.sendByWeChat(content);
}
遗留问题:
- 新增消息类型需要修改
NotificationService
- 新增渠道类型需要修改
EmergencyMessage
- 两种修改都会影响核心业务类
产生的影响
- 类爆炸风险:每新增渠道需修改所有消息类(N×M扩展)
- 高耦合:消息业务逻辑与渠道实现深度耦合
- 维护困难:客户端存在多重条件判断
三、桥接模式重构方案
3.1 模式架构解析
桥接模式四要素
维度 | 说明 | 具体实现 |
---|---|---|
抽象部分(Abstraction) | 高层业务逻辑控制 | Notifier抽象类 |
实现部分(Implementor) | 底层具体实现 | Message接口 |
解耦桥梁 | 连接抽象与实现的组合关系 | Notifier持有Message引用 |
正交扩展 | 独立扩展消息类型和通知渠道 | 新增类型/渠道互不影响 |
3.2 重构实现步骤
步骤1:定义消息抽象接口
arduino
public interface Message {
String formatContent(String rawContent);
void send(String content);
}
步骤2:实现具体消息类型
typescript
// 普通消息
public class NormalMessage implements Message {
private String template = "【普通】%s";
@Override
public String format(String rawContent) {
return String.format(template, rawContent);
}
@Override
public void send(String content) {
String format = format(content);
System.out.println("发送消息: " + format);
}
public void setTemplate(String template) {
this.template = template;
}
}
// 紧急消息
public class EmergencyMessage implements Message {
@Override
public String format(String content) {
return "【紧急】" + content + "(优先级:高)";
}
@Override
public void send(String content) {
String sendContent = this.format(content);
System.out.println("发送消息: " + sendContent);
}
}
步骤3:定义渠道抽象类
csharp
public abstract class Notifier {
protected Message message;
public Notifier(Message message) {
this.message = message;
}
public abstract void notify(String rawContent);
}
步骤4:实现具体渠道
java
// 极光推送
public class JPushNotifier extends Notifier {
public JPushNotifier(Message message) {
super(message);
}
@Override
public void notify(String content) {
System.out.print("[极光渠道] ");
System.out.print("[极光] 格式转换 -> ");
message.send(content);
}
}
// 短信通知
public class SMSNotifier extends Notifier {
public SMSNotifier(Message message) {
super(message);
}
@Override
public void notify(String content) {
System.out.print("[短信渠道] ");
message.send(content);
}
}
// 微信通知(新增渠道)
public class WeChatNotifier extends Notifier {
public WeChatNotifier(Message message) {
super(message);
}
@Override
public void notify(String content) {
System.out.print("[微信渠道] ");
System.out.print("[微信] 加密处理 -> ");
message.send(content);
}
}
3.3 客户端调用示例
java
public class MessageTest {
@Test
public void testNormalMessage() {
Message normalMsg = new NormalMessage();
Message emergencyMsg = new EmergencyMessage();
Notifier jpush = new JPushNotifier(normalMsg);
jpush.notify("订单状态更新:已发货");
Notifier wechat = new WeChatNotifier(emergencyMsg);
wechat.notify("服务器CPU使用率超过95%!");
SMSNotifier smsNotifier = new SMSNotifier(normalMsg);
smsNotifier.notify("用户注册成功!");
}
}
执行结果:
ini
[极光渠道] [极光] 格式转换 -> 发送消息: 【普通消息】订单状态更新:已发货
[微信渠道] [微信] 加密处理 -> 发送消息: 【紧急】服务器CPU使用率超过95%!(优先级:高)
[短信渠道] 发送消息: 【普通消息】用户注册成功!
四、重构效果对比
指标 | 重构前 | 重构后 |
---|---|---|
类数量(3渠道2类型) | 3×2=6个 | 3+2=5个 |
新增渠道成本 | 修改所有消息类 | 新增1个渠道类 |
新增消息类型成本 | 修改所有渠道 | 新增1个消息类 |
核心业务类修改点 | 5处 | 0处 |
五、Spring 事务管理模块
1. 抽象部分定义
java
// PlatformTransactionManager.java
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
2. 具体实现示例
java
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {
private DataSource dataSource; // 桥接点
// 通过组合连接数据源实现
public DataSourceTransactionManager(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
protected Object doGetTransaction() {
// 获取JDBC连接实现
ConnectionHolder conHolder = TransactionSynchronizationManager.getResource(dataSource);
return new DataSourceTransactionObject(conHolder);
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
// 使用具体数据源实现开启事务
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = DataSourceUtils.getConnection(dataSource);
txObject.setConnectionHolder(new ConnectionHolder(con), true);
}
}
3. 客户端使用
typescript
// 声明式事务配置
@Configuration
@EnableTransactionManagement
public class AppConfig {
// 抽象接口:PlatformTransactionManager
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
// 具体实现:DataSourceTransactionManager
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
长话短说
核心思想
将抽象与实现解耦,使二者可以独立变化。通过组合代替继承,避免因多层继承导致的类爆炸问题。
抽象层 :定义高层次的逻辑接口(如业务功能)。
实现层 :提供底层具体实现(如技术细节)。两者通过组合关系连接,而非强耦合的继承关系。
如何使用??
- 拆分抽象与实现
- 抽象层:定义业务接口。
- 实现层:定义底层接口。
- 通过组合关联两者
- 抽象类中持有实现层接口的引用。
- 独立扩展子类
- 抽象层的子类实现业务逻辑。
- 实现层的子类提供具体技术实现。
- 客户端动态组合
在调用时,将抽象对象与具体实现对象组合使用(如 new 抽象层的子类(new 实现层的子类())))。
何时使用??
选择桥接模式
- 当系统存在两个以上独立变化的维度时
- 需要避免多层继承导致的类爆炸问题时
- 期望在运行时切换实现方式时
避免使用桥接模式
- 变化维度存在强耦合关系
- 系统复杂度不足以需要解耦
- 性能敏感场景(桥接可能带来间接调用开销)