设计模式-桥接模式

写在前面

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
  • 两种修改都会影响核心业务类

产生的影响

  1. 类爆炸风险:每新增渠道需修改所有消息类(N×M扩展)
  2. 高耦合:消息业务逻辑与渠道实现深度耦合
  3. 维护困难:客户端存在多重条件判断

三、桥接模式重构方案

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();
    }
}

长话短说

核心思想

将抽象与实现解耦‌,使二者可以独立变化。通过‌组合代替继承‌,避免因多层继承导致的类爆炸问题。

抽象层‌ :定义高层次的逻辑接口(如业务功能)。
实现层‌ :提供底层具体实现(如技术细节)。

两者通过‌组合关系‌连接,而非强耦合的继承关系。

如何使用??

  1. 拆分抽象与实现‌
  • 抽象层‌:定义业务接口。
  • 实现层‌:定义底层接口。
  1. 通过组合关联两者‌
  • 抽象类中持有实现层接口的引用。
  1. 独立扩展子类‌
  • 抽象层的子类实现业务逻辑。
  • 实现层的子类提供具体技术实现。
  1. 客户端动态组合‌
    在调用时,将抽象对象与具体实现对象组合使用(如 new 抽象层的子类(new 实现层的子类())))。

何时使用??

选择桥接模式
  1. 当系统存在两个以上独立变化的维度时
  2. 需要避免多层继承导致的类爆炸问题时
  3. 期望在运行时切换实现方式时
避免使用桥接模式
  1. 变化维度存在强耦合关系
  2. 系统复杂度不足以需要解耦
  3. 性能敏感场景(桥接可能带来间接调用开销)
相关推荐
小乌龟不会飞35 分钟前
【SpringBoot】统一功能处理
java·spring boot·后端
刘小吉39 分钟前
java net 配置局域网受信任的https
后端
考虑考虑1 小时前
JPA中的EntityGraph
spring boot·后端·spring
coolflyr_reg1 小时前
禅道集成Firebase PHP-JWT
后端
似水流年流不尽思念1 小时前
常见的排序算法有哪些?它们的平均时间复杂度是多少?
后端·算法
孟永峰_Java2 小时前
MySQL 组合IN查询:你的索引为什么罢工了?
后端
ruokkk2 小时前
一个困扰我多年的Session超时Bug,被我的新AI搭档半天搞定了
javascript·后端·架构
楽码2 小时前
端到端应用Hmac加密
服务器·后端·算法
孟永峰_Java2 小时前
Java程序员的周五:代码没写完,但我的心已经放假了!
后端
uhakadotcom2 小时前
Flink有python的SDK入门教程
后端·面试·github