【设计模式】【结构型模式】桥接模式(Bridge)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

一、入门

什么是桥接模式?

桥接模式(Bridge Pattern)是一种结构型设计模式 ,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座"桥"连接了两个维度的变化,避免用继承导致代码臃肿。

为什么要用桥接模式?

这次我以大家熟悉的支付系统为例,详细说明桥接模式如何解决现实中的问题。假设我们要实现一个支持不同支付方式(支付宝、微信)和不同支付渠道(国内支付、跨境支付)的系统。
传统实现:支付方式 × 渠道 = 类爆炸:以下为代码实现

java 复制代码
// 支付宝国内支付
class AlipayDomestic {
    void pay() { 
        System.out.println("支付宝国内支付:调用国内风控API");
        System.out.println("支付宝国内支付:人民币结算");
    }
}

// 支付宝跨境支付
class AlipayCrossBorder {
    void pay() { 
        System.out.println("支付宝跨境支付:调用国际风控API");
        System.out.println("支付宝跨境支付:美元结算");
    }
}

// 微信国内支付
class WechatDomestic {
    void pay() { 
        System.out.println("微信国内支付:调用国内风控API");
        System.out.println("微信国内支付:人民币结算");
    }
}

// 微信跨境支付
class WechatCrossBorder {
    void pay() { 
        System.out.println("微信跨境支付:调用国际风控API");
        System.out.println("微信跨境支付:美元结算");
    }
}

// 如果新增一个银联支付,需要再写两个类...
// 如果新增一个支付渠道(如港澳台专线),需要为每个支付方式创建新类...

存在问题:

  1. 重复代码:国内/跨境支付的风控和结算逻辑被重复写在每个类中。
  2. 维护困难:修改国内支付的结算规则,需要改动所有相关类。
  3. 扩展性差:新增支付方式或渠道时,需要创建大量新类。

如何实现桥接模式?

桥接(Bridge)模式包含以下主要角色

  • 抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。

【案例】桥接模式- 改

桥接模式解法:解耦支付方式与渠道

核心思想

  • 抽象部分:支付方式(支付宝、微信),关注用户侧操作。
  • 实现部分:支付渠道(国内、跨境),关注底层金融操作。

抽象化 (Abstraction) : PaymentMethod类,定义高层抽象接口,持有实现层对象引用。

java 复制代码
abstract class PaymentMethod {
    protected PaymentChannel channel; // 关键:桥接的核心
    
    public PaymentMethod(PaymentChannel channel) {
        this.channel = channel;
    }
    
    public final void pay() {
        channel.applyRiskControl(); // 调用实现层
        doPayment();                // 自身逻辑
        channel.settleFunds();      // 调用实现层
    }
    
    protected abstract void doPayment();
}

扩展抽象化 (Refined Abstraction)Alipay类、WechatPay类。实现并扩展抽象层的业务逻辑。

java 复制代码
// 支付宝支付
class Alipay extends PaymentMethod {
    public Alipay(PaymentChannel channel) {
        super(channel);
    }
    
    protected void doPayment() {
        System.out.println("支付宝:扫码用户付款码,扣除余额");
    }
}

// 微信支付
class WechatPay extends PaymentMethod {
    public WechatPay(PaymentChannel channel) {
        super(channel);
    }
    
    protected void doPayment() {
        System.out.println("微信:调用微信SDK,从零钱扣款");
    }
}

实现化 (Implementor)PaymentChannel接口。定义底层操作的接口规范(如风控和结算),独立于抽象层变化(支付方式如何变化不影响渠道实现)。

java 复制代码
interface PaymentChannel {
    void applyRiskControl(); // 风控接口
    void settleFunds();      // 结算接口
}
具体实现化 (Concrete Implementor):DomesticChannel, CrossBorderChannel类。具体实现底层操作(如国内渠道的身份证验证),可独立扩展(新增渠道无需修改抽象层)。
// 国内支付渠道
class DomesticChannel implements PaymentChannel {
    public void applyRiskControl() {
        System.out.println("国内风控:验证身份证 + 银行卡绑定");
    }
    
    public void settleFunds() {
        System.out.println("结算:人民币入账,T+1到账");
    }
}

// 跨境支付渠道
class CrossBorderChannel implements PaymentChannel {
    public void applyRiskControl() {
        System.out.println("跨境风控:验证护照 + 外汇管制检查");
    }
    
    public void settleFunds() {
        System.out.println("结算:美元入账,汇率转换,T+3到账");
    }
}

客户端

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 创建支付渠道(实现层)
        PaymentChannel domestic = new DomesticChannel();
        PaymentChannel crossBorder = new CrossBorderChannel();
        
        // 组合支付方式和渠道(抽象层)
        PaymentMethod alipayDomestic = new Alipay(domestic);
        PaymentMethod wechatCrossBorder = new WechatPay(crossBorder);
        
        // 执行支付
        alipayDomestic.pay();
        /* 输出:
           开始支付流程...
           国内风控:验证身份证 + 银行卡绑定
           支付宝:扫码用户付款码,扣除余额
           结算:人民币入账,T+1到账
           支付完成!
        */
        
        wechatCrossBorder.pay();
        /* 输出:
           开始支付流程...
           跨境风控:验证护照 + 外汇管制检查
           微信:调用微信SDK,从零钱扣款
           结算:美元入账,汇率转换,T+3到账
           支付完成!
        */
    }
}

这里肯定会有UU说,我好像用装饰器模式也能实现

确实,装饰器模式在某些场景下可能与桥接模式有相似之处,但它们的核心意图和适用场景有本质区别。让我通过对比来帮你理清这两种模式的区别,以及为什么在支付系统的例子中桥接模式更合适。

桥接模式 装饰器模式
核心目的 分离抽象与实现,让两个维度独立扩展 动态地为对象添加额外功能
关系结构 组合关系(横向扩展) 嵌套装饰(纵向扩展)
适用场景 多个正交变化的维度(如形状×渲染方式) 需要动态叠加功能(如咖啡加糖、加奶)
设计重点 解耦抽象层与实现层 增强对象的功能

二、桥接模式在框架源码中的运用

JDBC(Java 数据库连接)

JDBC 桥接模式工作流程

加载驱动实现类(具体实现化)

java 复制代码
Class.forName("com.mysql.cj.jdbc.Driver"); // 触发MySQL驱动的static块注册

获取抽象层接口(桥接器选择具体实现)

java 复制代码
Connection conn = DriverManager.getConnection(
    "jdbc:mysql://localhost:3306/mydb", "user", "password"
);
  • DriverManager根据URL jdbc:mysql: 找到MySQL驱动
  • 调用MySQL Driver.connect()方法,返回ConnectionImpl实例(但应用程序只看到Connection接口)

操作数据库(通过抽象层接口)

java 复制代码
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
  • 无论底层是MySQL还是Oracle,代码完全一致

JDBC 的源码实现

桥接模式角色 JDBC 中的对应组件 具体职责
抽象化 (Abstraction) java.sql.Connection等接口 定义数据库操作的高层抽象
扩展抽象化 (Refined Abstraction) 无(直接使用接口) JDBC接口已足够抽象,无需扩展
实现化 (Implementor) java.sql.Driver 接口 定义数据库驱动实现的规范
具体实现化 (Concrete Implementor) 各数据库驱动实现类(如com.mysql.cj.jdbc.Driver) 具体实现数据库连接和操作逻辑
桥接器 (Bridge) DriverManager 连接抽象层和实现层的桥梁

抽象化 (Abstraction)

  • 对应组件:java.sql.ConnectionStatementPreparedStatement 等接口
  • 核心职责:
    • 定义数据库操作的抽象接口(如执行SQL、事务管理)
    • 完全独立于具体数据库(无论是MySQL还是Oracle,接口方法一致)
java 复制代码
public interface Connection extends Wrapper, AutoCloseable {
    Statement createStatement() throws SQLException;
    PreparedStatement prepareStatement(String sql) throws SQLException;
    void commit() throws SQLException;
    // 其他数据库操作抽象方法...
}

实现化 (Implementor)

  • 对应接口:java.sql.Driver
  • 核心职责:
    • 定义数据库驱动的规范(如何连接数据库、创建连接对象)
    • 由各数据库厂商实现(如MySQL、Oracle、PostgreSQL)
java 复制代码
public interface Driver {
    Connection connect(String url, Properties info) throws SQLException;
    boolean acceptsURL(String url) throws SQLException;
    // 其他驱动核心方法...
}

具体实现化 (Concrete Implementor)

  • 对应类 :各数据库的驱动实现类
    • MySQL: com.mysql.cj.jdbc.Driver
    • Oracle: oracle.jdbc.OracleDriver
    • PostgreSQL: org.postgresql.Driver
  • 核心职责
    • 实现Driver接口,提供具体的数据库连接逻辑
    • 隐藏数据库底层差异(如MySQL的协议、Oracle的专有语法)
java 复制代码
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
   static {
       // 注册驱动到DriverManager
       DriverManager.registerDriver(new Driver());
   }
   
   public Connection connect(String url, Properties info) throws SQLException {
       // 创建MySQL专属的ConnectionImpl对象
       return new ConnectionImpl(url, info);
   }
}

桥接器 (Bridge)

  • 对应类java.sql.DriverManager
  • 核心职责
    • 管理所有注册的数据库驱动(Driver实现类)
    • 根据URL选择合适的具体驱动,返回抽象层接口(如Connection

关键代码逻辑:

java 复制代码
public class DriverManager {
   private static final List<Driver> drivers = new CopyOnWriteArrayList<>();
   
   // 注册驱动
   public static void registerDriver(Driver driver) {
       drivers.add(driver);
   }
   
   // 获取连接(核心桥接逻辑)
   public static Connection getConnection(String url, String user, String password) {
       for (Driver driver : drivers) {
           if (driver.acceptsURL(url)) {
               // 调用具体驱动的connect方法
               return driver.connect(url, properties);
           }
       }
       throw new SQLException("No suitable driver found for " + url);
   }
}

Spring Web MVC

桥接模式的应用

  • 抽象层:HandlerAdapter(处理器适配器接口)
  • 实现层:不同处理器类型的适配器(如RequestMappingHandlerAdapter
java 复制代码
// 抽象层:HandlerAdapter接口
public interface HandlerAdapter {
    boolean supports(Object handler);
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

// 实现层:RequestMappingHandlerAdapter
public class RequestMappingHandlerAdapter implements HandlerAdapter {
    public boolean supports(Object handler) {
        return handler instanceof HandlerMethod;
    }
    
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 处理@RequestMapping注解的方法
    }
}

桥接模式的作用

  • 解耦:DispatcherServlet通过HandlerAdapter调用处理器,无需关心具体处理器的类型(如@ControllerHttpRequestHandler)。
  • 扩展性:新增处理器类型只需实现HandlerAdapter接口,无需修改DispatcherServlet

三、总结

桥接模式的优点

  1. 解耦抽象与实现
    • 抽象层和实现层可以独立变化,互不影响。
    • 例如,JDBC中Connection接口(抽象层)和Driver实现(实现层)可以分别扩展。
  2. 提高扩展性
    • 新增抽象类或实现类时,无需修改现有代码,符合开闭原则。
    • 例如,新增数据库驱动只需实现Driver接口,无需修改JDBC核心代码。
  3. 减少类爆炸
    • 通过组合代替继承,避免了多层继承导致的类数量暴增。
    • 例如,支付系统中支付方式和支付渠道的组合只需M+N个类,而不是M×N个类。
  4. 运行时动态绑定
    • 可以在运行时动态切换实现。
    • 例如,JDBC中DriverManager根据URL动态选择具体的数据库驱动。
  5. 隐藏实现细节
    • 客户端只依赖抽象接口,无需关心具体实现。
    • 例如,应用程序只使用Connection接口,无需关心底层是MySQL还是Oracle。

桥接模式的缺点

  1. 增加系统复杂性
    • 需要额外设计抽象层和实现层,增加了代码量和理解难度。
    • 对于简单场景,可能显得过度设计。
  2. 设计难度较高
    • 需要准确识别系统中独立变化的维度,设计不当可能导致模式滥用。
    • 例如,如果错误地将两个强耦合的维度分离,反而会增加维护成本。
  3. 性能开销
    • 由于抽象层和实现层通过组合连接,可能会引入额外的间接调用,带来轻微的性能开销。

桥接模式适用场景

  1. 多个独立变化的维度
    • 当系统中存在两个或多个正交变化的维度,且每个维度都可能独立扩展时。
    • 经典案例:
      • 支付系统:支付方式 × 支付渠道
      • 图形库:形状 × 渲染方式
      • 跨平台UI框架:UI组件 × 操作系统API
  2. 避免多层继承
    • 当使用继承会导致类数量爆炸时,桥接模式是更好的选择。
    • 经典案例:
      • JDBC:Connection接口 × 数据库驱动
      • 物流系统:运输方式 × 物流公司
  3. 运行时切换实现
    • 当需要在运行时动态切换实现时。
    • 经典案例:
      • 日志框架:日志接口 × 日志实现(Logback、Log4j)
      • 消息通知系统:通知类型 × 发送渠道(短信、邮件、App推送)
  4. 隐藏实现细节
    • 当需要对外暴露简洁的接口,同时隐藏复杂的实现细节时。
    • 经典案例:
      • 数据库连接池:连接池接口 × 具体连接池实现(HikariCP、Druid)
      • 缓存框架:缓存接口 × 缓存实现(Redis、Memcached)
相关推荐
星星点点洲16 分钟前
【操作幂等和数据一致性】保障业务在MySQL和COS对象存储的一致
java·mysql
xiaolingting33 分钟前
JVM层面的JAVA类和实例(Klass-OOP)
java·jvm·oop·klass·instanceklass·class对象
风口上的猪20151 小时前
thingboard告警信息格式美化
java·服务器·前端
追光少年33221 小时前
迭代器模式
java·迭代器模式
超爱吃士力架2 小时前
MySQL 中的回表是什么?
java·后端·面试
付聪12102 小时前
装饰器模式
设计模式
扣丁梦想家2 小时前
设计模式教程:外观模式(Facade Pattern)
设计模式·外观模式
扣丁梦想家3 小时前
设计模式教程:装饰器模式(Decorator Pattern)
java·前端·装饰器模式
drebander3 小时前
Maven 构建中的安全性与合规性检查
java·maven
drebander3 小时前
Maven 与 Kubernetes 部署:构建和部署到 Kubernetes 环境中
java·kubernetes·maven