设计模式-桥接模式

写在前面

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. 性能敏感场景(桥接可能带来间接调用开销)
相关推荐
Anarkh_Lee几秒前
图解JVM - 13.垃圾回收器
java·jvm·后端
五行星辰27 分钟前
SpringBoot与Redis联姻:从青铜到王者的配置婚庆指南!
java·后端
牛肉汤31 分钟前
联邦式架构中的协议抉择:从 Mastodon 的设计看分布式系统的技术权衡
后端
吹风的坚果34 分钟前
HTTPS的那些事
后端·https
五行星辰38 分钟前
Redisson:Redis界的变形金刚,会变身还会唱跳Rap!
java·后端
BUG研究员_39 分钟前
Spring Boot自动装配原理
java·spring boot·后端
L7ink40 分钟前
FinBot - WeChatFerry + DeepSeek打造 个人智能记账助手
后端·deepseek
五行星辰40 分钟前
Jedis大战Redis:码农版《星球大战》开演
java·后端
uhakadotcom1 小时前
Supabase数据库性能优化全攻略:从慢查询到闪电响应的实战技巧
后端·面试·github
YGGP1 小时前
【每日八股】Golang篇(二):关键字(上)
开发语言·后端·golang