【HeadFirst系列之HeadFirst设计模式】第15天之桥接模式:让代码的扩展更优雅!

Scroll Down

桥接模式:让代码的扩展更优雅!

在软件开发中,我们经常遇到多维度变化的需求,如果不合理地设计代码结构,随着需求的扩展,代码会变得越来越复杂,难以维护。桥接模式(Bridge Pattern) 就是一种强大的结构型设计模式,能够有效地解耦代码中的抽象部分(Abstraction)实现部分(Implementation),让它们可以独立变化,提高代码的扩展性和灵活性。

在本文中,我们将从实际问题出发,探讨如何通过桥接模式优化代码结构,并结合 JDKSpring 框架 的应用场景,帮助你更好地理解和掌握这一模式。


1. 现实中的问题:多维度变化导致的代码膨胀

场景描述

假设我们正在开发一款跨平台的消息发送系统,支持以下功能:

  1. 消息类别:普通消息、加急消息、特急消息。
  2. 消息发送渠道:短信(SMS)、邮件(Email)、应用内通知(App Notification)。

一个直接的设计方式是创建不同的类来表示各种组合,比如:

class SmsCommonMessage { }
class SmsUrgentMessage { }
class SmsVeryUrgentMessage { }
class EmailCommonMessage { }
class EmailUrgentMessage { }
class EmailVeryUrgentMessage { }
class AppCommonMessage { }
class AppUrgentMessage { }
class AppVeryUrgentMessage { }

这样一来,随着消息类别发送渠道的增加,我们需要创建的类数会呈现指数级增长,代码变得臃肿且难以维护。


2. 解决方案:引入桥接模式

桥接模式(Bridge Pattern) 主要用于将抽象部分与实现部分分离,使它们可以独立地变化,从而减少代码的复杂度。

核心思想

  1. 将“消息类别”作为抽象部分(Abstraction)
  2. 将“消息发送渠道”作为实现部分(Implementation)
  3. 通过组合的方式,将二者关联起来,而不是继承

桥接模式的 UML 结构

                ┌────────────────┐
                │  Abstraction    │
                ├────────────────┤
                │  sendMessage()  │
                └────────────────┘
                         │
                         ▼
                ┌──────────────────┐
                │  RefinedAbstraction │
                ├──────────────────┤
                │  sendMessage()  │
                └──────────────────┘
                         │
         ┌──────────────┴──────────────┐
         ▼                              ▼
┌────────────────┐          ┌────────────────┐
│  Implementor    │          │  ConcreteImplementor │
├────────────────┤          ├────────────────┤
│  send()        │          │  send()        │
└────────────────┘          └────────────────┘

3. 使用 Java 代码实现桥接模式

我们将 消息类别 作为抽象部分,消息发送渠道 作为实现部分,并通过组合来建立它们之间的桥梁。

(1)定义消息发送渠道(实现部分 Implementor)

// 定义消息发送接口
interface MessageSender {
    void send(String message);
}

// 短信发送实现
class SmsSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("发送短信: " + message);
    }
}

// 邮件发送实现
class EmailSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("发送邮件: " + message);
    }
}

// 应用内通知实现
class AppNotificationSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("发送应用通知: " + message);
    }
}

(2)定义消息类型(抽象部分 Abstraction)

// 抽象消息类
abstract class Message {
    protected MessageSender sender;

    public Message(MessageSender sender) {
        this.sender = sender;
    }

    abstract void sendMessage(String message);
}

// 普通消息
class CommonMessage extends Message {
    public CommonMessage(MessageSender sender) {
        super(sender);
    }

    @Override
    void sendMessage(String message) {
        System.out.print("[普通消息] ");
        sender.send(message);
    }
}

// 加急消息
class UrgentMessage extends Message {
    public UrgentMessage(MessageSender sender) {
        super(sender);
    }

    @Override
    void sendMessage(String message) {
        System.out.print("[加急消息] ");
        sender.send(message);
    }
}

(3)测试桥接模式

public class BridgePatternTest {
    public static void main(String[] args) {
        // 发送普通消息(短信)
        Message message1 = new CommonMessage(new SmsSender());
        message1.sendMessage("请假申请");

        // 发送加急消息(邮件)
        Message message2 = new UrgentMessage(new EmailSender());
        message2.sendMessage("服务器宕机警报!");
    }
}

运行结果

[普通消息] 发送短信: 请假申请
[加急消息] 发送邮件: 服务器宕机警报!

4. 桥接模式的优缺点

✅ 优点

  1. 降低代码耦合度:消息类型和发送渠道可以独立变化,扩展性更强。
  2. 符合开闭原则(OCP):新增消息类型或发送方式时,只需创建新类,而不影响已有代码。
  3. 符合单一职责原则(SRP):不同职责的类不会混在一起,代码更清晰。

❌ 缺点

  1. 增加了代码复杂度:相比简单继承方案,桥接模式需要额外的类和接口,初学者可能不易理解。
  2. 适用于扩展性需求较高的场景:如果消息类型和发送渠道不会频繁变化,则直接使用继承可能更简单。

5. 桥接模式在 JDK 和 Spring 中的应用

5.1 JDK 中的桥接模式应用

(1) Java AWT 框架

Java 的 Abstract Window Toolkit(AWT) 使用桥接模式来处理不同操作系统上的 GUI 显示:

  • java.awt.Button(抽象部分)
  • java.awt.peer.ButtonPeer(实现部分)

这样,Java AWT 可以在不同操作系统(Windows、Mac、Linux)上无缝运行,而无需修改代码


5.2 Spring 框架中的桥接模式

(2) JDBC(Java Database Connectivity)

Spring 的 JDBC 访问策略 也是桥接模式的应用:

  • 抽象部分JdbcTemplate
  • 实现部分:不同数据库驱动(DriverManagerDataSourceHikariDataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate(new HikariDataSource());

这样,Spring 不依赖具体数据库,可以轻松切换不同的数据库驱动(MySQL、PostgreSQL、Oracle等)。


6. 总结

  1. 桥接模式用于解耦多个维度变化的系统,使它们可以独立扩展
  2. 在JDK、Spring等框架中被广泛使用,提高了系统的灵活性和可扩展性
  3. 适用于需要多维度扩展的场景,如 GUI、数据库连接、消息系统等

💡 你是否在项目中遇到类似的扩展问题?尝试使用桥接模式优化你的代码吧! 🚀