设计模式-桥接模式

写在前面

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. 性能敏感场景(桥接可能带来间接调用开销)
相关推荐
君爱学习42 分钟前
RocketMQ延迟消息是如何实现的?
后端
Falling421 小时前
使用 CNB 构建并部署maven项目
后端
程序员小假1 小时前
我们来讲一讲 ConcurrentHashMap
后端
爱上语文1 小时前
Redis基础(5):Redis的Java客户端
java·开发语言·数据库·redis·后端
萧曵 丶1 小时前
Rust 中的返回类型
开发语言·后端·rust
高兴达3 小时前
Spring boot入门工程
java·spring boot·后端
到账一个亿4 小时前
后端树形结构
后端
武子康5 小时前
大数据-31 ZooKeeper 内部原理 Leader选举 ZAB协议
大数据·后端·zookeeper
我是哪吒5 小时前
分布式微服务系统架构第155集:JavaPlus技术文档平台日更-Java线程池实现原理
后端·面试·github