【设计模式】【结构型模式】桥接模式(Bridge)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 什么是桥接模式?
- 为什么要用桥接模式?
- 如何实现桥接模式?
- 二、桥接模式在框架源码中的运用
- JDBC(Java 数据库连接)
- JDBC 桥接模式工作流程
- JDBC 的源码实现
- Spring Web MVC
- 三、总结
- 桥接模式的优点
- 桥接模式的缺点
- 桥接模式适用场景
一、入门
什么是桥接模式?
桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代码臃肿。
为什么要用桥接模式?
这次我以大家熟悉的支付系统为例,详细说明桥接模式如何解决现实中的问题。假设我们要实现一个支持不同支付方式(支付宝、微信)和不同支付渠道(国内支付、跨境支付)的系统。
传统实现:支付方式 × 渠道 = 类爆炸:以下为代码实现
// 支付宝国内支付
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("微信跨境支付:美元结算");}
}// 如果新增一个银联支付,需要再写两个类...
// 如果新增一个支付渠道(如港澳台专线),需要为每个支付方式创建新类...
存在问题:
- 重复代码:国内/跨境支付的风控和结算逻辑被重复写在每个类中。
- 维护困难:修改国内支付的结算规则,需要改动所有相关类。
- 扩展性差:新增支付方式或渠道时,需要创建大量新类。
如何实现桥接模式?
桥接(Bridge)模式包含以下主要角色:
- 抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。
【案例】桥接模式- 改

桥接模式解法:解耦支付方式与渠道
核心思想
- 抽象部分:支付方式(支付宝、微信),关注用户侧操作。
- 实现部分:支付渠道(国内、跨境),关注底层金融操作。
抽象化 (Abstraction): PaymentMethod类,定义高层抽象接口,持有实现层对象引用。
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类。实现并扩展抽象层的业务逻辑。
// 支付宝支付
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接口。定义底层操作的接口规范(如风控和结算),独立于抽象层变化(支付方式如何变化不影响渠道实现)。
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到账");}
}
客户端
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 桥接模式工作流程
加载驱动实现类(具体实现化)
Class.forName("com.mysql.cj.jdbc.Driver"); // 触发MySQL驱动的static块注册
获取抽象层接口(桥接器选择具体实现)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password"
);
DriverManager根据URLjdbc:mysql:找到MySQL驱动- 调用
MySQL Driver.connect()方法,返回ConnectionImpl实例(但应用程序只看到Connection接口)
操作数据库(通过抽象层接口)
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.Connection、Statement、PreparedStatement等接口 - 核心职责:
- 定义数据库操作的抽象接口(如执行SQL、事务管理)
- 完全独立于具体数据库(无论是MySQL还是Oracle,接口方法一致)
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)
public interface Driver {Connection connect(String url, Properties info) throws SQLException;boolean acceptsURL(String url) throws SQLException;// 其他驱动核心方法...
}
具体实现化 (Concrete Implementor)
- 对应类:各数据库的驱动实现类
MySQL: com.mysql.cj.jdbc.DriverOracle: oracle.jdbc.OracleDriverPostgreSQL: org.postgresql.Driver
- 核心职责:
- 实现Driver接口,提供具体的数据库连接逻辑
- 隐藏数据库底层差异(如MySQL的协议、Oracle的专有语法)
public class Driver extends NonRegisteringDriver implements java.sql.Driver {static {// 注册驱动到DriverManagerDriverManager.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)
- 管理所有注册的数据库驱动(
关键代码逻辑:
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)
// 抽象层: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调用处理器,无需关心具体处理器的类型(如@Controller、HttpRequestHandler)。 - 扩展性:新增处理器类型只需实现
HandlerAdapter接口,无需修改DispatcherServlet。
三、总结
桥接模式的优点
- 解耦抽象与实现
- 抽象层和实现层可以独立变化,互不影响。
- 例如,JDBC中Connection接口(抽象层)和Driver实现(实现层)可以分别扩展。
- 提高扩展性
- 新增抽象类或实现类时,无需修改现有代码,符合开闭原则。
- 例如,新增数据库驱动只需实现Driver接口,无需修改JDBC核心代码。
- 减少类爆炸
- 通过组合代替继承,避免了多层继承导致的类数量暴增。
- 例如,支付系统中支付方式和支付渠道的组合只需M+N个类,而不是M×N个类。
- 运行时动态绑定
- 可以在运行时动态切换实现。
- 例如,JDBC中DriverManager根据URL动态选择具体的数据库驱动。
- 隐藏实现细节
- 客户端只依赖抽象接口,无需关心具体实现。
- 例如,应用程序只使用Connection接口,无需关心底层是MySQL还是Oracle。
桥接模式的缺点
- 增加系统复杂性
- 需要额外设计抽象层和实现层,增加了代码量和理解难度。
- 对于简单场景,可能显得过度设计。
- 设计难度较高
- 需要准确识别系统中独立变化的维度,设计不当可能导致模式滥用。
- 例如,如果错误地将两个强耦合的维度分离,反而会增加维护成本。
- 性能开销
- 由于抽象层和实现层通过组合连接,可能会引入额外的间接调用,带来轻微的性能开销。
桥接模式适用场景
- 多个独立变化的维度
- 当系统中存在两个或多个正交变化的维度,且每个维度都可能独立扩展时。
- 经典案例:
- 支付系统:支付方式 × 支付渠道
- 图形库:形状 × 渲染方式
- 跨平台UI框架:UI组件 × 操作系统API
- 避免多层继承
- 当使用继承会导致类数量爆炸时,桥接模式是更好的选择。
- 经典案例:
- JDBC:Connection接口 × 数据库驱动
- 物流系统:运输方式 × 物流公司
- 运行时切换实现
- 当需要在运行时动态切换实现时。
- 经典案例:
- 日志框架:日志接口 × 日志实现(Logback、Log4j)
- 消息通知系统:通知类型 × 发送渠道(短信、邮件、App推送)
- 隐藏实现细节
- 当需要对外暴露简洁的接口,同时隐藏复杂的实现细节时。
- 经典案例:
- 数据库连接池:连接池接口 × 具体连接池实现(HikariCP、Druid)
- 缓存框架:缓存接口 × 缓存实现(Redis、Memcached)
相关文章:
【设计模式】【结构型模式】桥接模式(Bridge)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 👍 欢迎点赞、收藏、关注,跟上我的更新节奏 🎵 当你的天空突…...
惠普HP Color LaserJet CP1215/1210彩色打印机打印校准方法
执行校准 (用随机光盘安装驱动)完整安装打印机驱动程序。安装驱动程序的操作方法请参考以下文章: 惠普HP Color laserjet cp1215激光打印机在windows 7下使用随机光盘安装驱动程序,安装完成后; 依次点击“开始”→“所…...
【雅思博客02】Virus!
Elementary ‐ Virus! (C0007) A: Oh great! This stupid computer froze again! That’s the third time today! Hey Samuel, can you come take a look at my PC? It’s acting up again. It must have a virus or something. B: Just give me a second; I’ll be right …...
模型GPU->NPU(Ascend)迁移训练简述
目录 一、迁移训练流程图解 二、详细流程步骤 1. 模型训练与日志记录 2. 跨平台精度对齐对比 3. 问题定位与修复 4. 迭代验证 三、关键技术点 四、常见问题与解决方案 一、迁移训练流程图解 通过华为云的modelart进行运行环境选型 北京四使用GPU进行模型训练ÿ…...
skywalking实现原理
SkyWalking 是一个开源的分布式应用性能监控(APM)系统,主要用于微服务、云原生应用的性能监控、追踪和故障诊断。其实现原理涉及多个核心模块和技术,以下是 SkyWalking 的实现原理概述: 1. 采集数据(数据收…...
sql语言语法的学习
sql通用语法 sql分类 DDL(操作数据库和表) 操作数据库 操作表_查询 操作表_创建 举例: 操作表_删除 操作表_修改 DML(增删改表中数据) DML添加数据 DML删除数据...
3.buuctf [BSidesCF 2019]Kookie
进入题目页面如下 尝试弱口令密码登录,无果 显示无效密码 用题中给出的用户名和密码登录虽然成功但没得到flag 用burp suite抓包试试 看到username处显示cookie 题目说用admin登录 将username的值改为admin 拿到flag 最后拿到flag...
springboot245-springboot项目评审系统(源码+论文+PPT+部署讲解等)
💕💕作者: 爱笑学姐 💕💕个人简介:十年Java,Python美女程序员一枚,精通计算机专业前后端各类框架。 💕💕各类成品Java毕设 。javaweb,ssm…...
Dify+Ollama+DeepSeek部署本地大模型+知识库搭建
前言 上一篇文章《OllamaDeepSeek部署本地大模型》我们已经知道如何在本地搭建自己的大模型了,不过想要让大模型能够根据我们个人或者企业的数据情况做出精准的回答,我们还需要将自己的数据投喂给大模型才可以。本篇文章我们将会使用一个开源项目dify集…...
每日一题——不同路径的数目与矩阵最小路径和
机器人路径问题与矩阵最小路径和 1. 机器人路径问题题目描述示例示例 1示例 2 解题思路动态规划 代码实现复杂度分析 2. 矩阵的最小路径和题目描述示例示例 1示例 2 解题思路动态规划 代码实现复杂度分析 总结 1. 机器人路径问题 题目描述 一个机器人在 (m \times n) 大小的地…...
143,【3】 buuctf web [GYCTF2020]EasyThinking
进入靶场 一开始那个题目名字就想到了框架 扫描目录 访问后自动下载了 找源码 <?php namespace app\home\controller;use think\exception\ValidateException; use think\facade\Db; use think\facade\View; use app\common\model\User; use think\facade\Request; use …...
Windows逆向工程入门之栈指令与核心机制深度解析
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 Windows逆向工程入门:栈指令与核心机制深度解析 一、栈的核心概念与内存布局 二、栈操作指令详解 1. PUSH 与 POP 2. PUSHA/PUSHAD 与 POPA/POPAD 3. PUSHF/PUSHFD 与 P…...
腾讯混元hunyuan3d生成模型,本地搭建和使用
腾讯混元hunyuan3d生成模型,本地搭建和使用 腾讯混元hunyuan3d生成模型,本地搭建和使用一. 话不多说,我们直接上本地部署的详细步骤1. 将仓库拉到本地2. 下载2个模型, 注意这里推荐 先使用conda创建环境后在安装modelscope进行下载模型2.1 关于第一个模型我们要在 Hunyuan3D-1 …...
flutter image_cropper插件安装后 打包apk 报错命名空间问题
本篇文章主要讲解,Flutter安装完新依赖打包apk报错 A problem occurred configuring project ‘:image_cropper’. 命名空间问题的解决办法及原因说明。 日期:2025年2月15日 作者:任聪聪 一、报错现象: 报文信息: FAI…...
2025-2-15-4.5 链表(基础题)
文章目录 4.6 链表(基础题)206. 反转链表92. 反转链表II24. 两两交换链表中的节点 4.6 链表(基础题) 有一天我做题忽然发现要把一个数组完全倒着翻过来很费劲,今天看,链表为此而生啊。 206. 反转链表 题目…...
目标检测IoU阈值全解析:YOLO/DETR模型中的精度-召回率博弈与工程实践指南
一、技术原理与数学本质 IoU计算公式: IoU \frac{Area\ of\ Overlap}{Area\ of\ Union} \frac{A ∩ B}{A ∪ B}阈值选择悖论: 高阈值(0.6-0.75):减少误检(FP↓)但增加漏检(FN↑…...
免费大模型网站
腾讯元宝 腾讯元宝 秘塔搜索 秘塔搜索 超算互联网 超算互联网回答速度很慢 Chatbot Arena Chatbot Arena 大模型竞技场。...
【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第三节】
ISO 14229-1:2023 UDS诊断服务测试用例全解析(安全访问0x27服务) 作者:车端域控测试工程师 更新日期:2025-02-12 关键词:UDS安全访问、0x27服务、ISO 14229-1:2023、ECU安全验证 一、服务概述 安全访问服务࿰…...
macOS部署DeepSeek-r1
好奇,跟着网友们的操作试了一下 网上方案很多,主要参考的是这篇 DeepSeek 接入 PyCharm,轻松助力编程_pycharm deepseek-CSDN博客 方案是:PyCharm CodeGPT插件 DeepSeek-r1:1.5b 假设已经安装好了PyCharm PyCharm: the Pyth…...
java八股文-mysql
1. 索引 1.1 什么是索引 索引(index)是帮助Mysql高效获取数据的数据结构(有序).提高数据的检索效率,降低数据库的IO成本(不需要全表扫描).通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗. 1.2 mysql索引使用的B树? 1. 没有使用二叉树,最坏情况o&…...
YOLO11环境搭建CUDA12.6
1.安装CUDA和cuDNN 1.1安装CUDA 1.1.1查看当前你的电脑显卡支持的最高CUDA版本,后面的安装不能超过它 通过命令的方式查看 输入nvidia-smi 1.1.2 下载CUDA 官网地址:CUDA Toolkit Archive | NVIDIA Developer 选择cuda_12.6.3 下载完成后,如下: 安装,一直下一步即可:…...
OpenEuler学习笔记(三十二):在OpenEuler上搭建项目管理平台
在OpenEuler上搭建一个支持网页和手持访问、且支持用户功能自定义的项目管理平台,可以选择多种开源工具。以下是基于 Redmine 的搭建方案,Redmine 是一个灵活的项目管理工具,支持网页和移动端访问,并且可以通过插件扩展功能。 1.…...
LabVIEW中的icon.llb 库
icon.llb 库位于 C:\Program Files (x86)\National Instruments\LabVIEW 2019\vi.lib\Platform 目录下,是 LabVIEW 系统中的一个重要库。它的主要功能是与图标相关的操作,提供了一些实用的 VI 用于处理 LabVIEW 图标的显示、修改和设置。通过该库&#x…...
React Hooks 的两个坑点
React Hooks 使用注意事项 Area: Hooks Date: February 10, 2025 Important: 🌟🌟🌟 React Hooks 注意事项 要点: useState 的初始化值 只在第一次渲染时计算,并且这个值不会随着组件重新渲染而更新。useEffect 可…...
C语言的字符串偏移小记
通过C的指针,我们可以遍历一个很长的字符串。我们针对这个字符串可以按照字符串里面的终止符做切分。 首先我们来看下面的一段代码: #include <stdio.h> #include <stdlib.h> #include <stdio.h> #include <string.h>const ch…...
Python学习心得正则表达式及re模块的使用
一、正则表达式定义以及正则表达式的构成: 1.什么式正则表达式: 正则表达式:是一个比较特殊的字符序列,能够帮助用户检查一个字符串是否符合某种特殊的模式。 2.正则表达式的构成: 正则表达式是由元字符和限定字符…...
Oracle启动与关闭(基础操作)
11g读取参数文件的顺序 9i之前,只有静态参数文件pfile文件。 9i开始,引入了动态参数文件spfile文件。 Windows: %ORACLE_HOME%\database SPFILEORCL就是动态参数文件 Linux: $ORACLE_HOME/dbs 11g中,启动数据库。…...
第二月:学习 NumPy、Pandas 和 Matplotlib 是数据分析和科学计算的基础
以下是一个为期 **1 个月(30 天)**的详细学习计划,精确到每天的学习内容和练习作业,帮助你系统地掌握 NumPy、Pandas 和 Matplotlib 的核心功能。 第 1 周:NumPy 基础 Day 1:NumPy 简介与数组创建 学习内…...
AI 语言模型发展史:统计方法、RNN 与 Transformer 的技术演进
引言 自然语言处理(NLP)是 AI 领域的重要分支,而语言模型(Language Model, LM)是 NLP 的核心技术。语言模型经历了从 统计方法 到 RNN(循环神经网络),再到 Transformer 的演进&…...
新版电脑通过wepe安装系统
官方下载链接 WIN10下载 WIN11下载 微PE 启动盘制作 1:选择启动盘的设备 2:选择对应的U盘设备,点击安装就可以,建议大于8g 3:在上方链接下载需要安装的程序包,放入启动盘,按需 更新系统 …...
