设计模式 · 桥接模式(Bridge Pattern)java
设计模式 · 桥接模式(Bridge Pattern)java
前言
业务代码里大量 if-else,往往不是因为逻辑复杂,而是因为变化维度被写死在了一起。
以支付场景为例,支付渠道可能有微信、支付宝、银联,支付模式又可能有密码、刷脸、指纹。如果把这些组合都写进一个方法里,短期看实现很快,长期看每新增一种渠道或模式,都要改动原有代码,重复逻辑越来越多,维护成本也越来越高。
桥接模式要解决的正是这类问题:当系统中存在两个或多个可以独立变化的维度时,将它们拆分出来,通过组合建立连接,避免类数量和判断分支随着组合关系膨胀。
本文将以“支付渠道 × 支付模式”为例,讲解桥接模式如何把复杂的组合逻辑拆成清晰、可扩展的面向对象结构。
本文代码链接:https://github.com/likerhood/CodeDesignWork/tree/main/codedesign6.0-0 和6.0-1
一、核心定义
桥接模式(Bridge Pattern) 是一种结构型设计模式,将抽象部分与实现部分分离,使它们可以独立变化。
- 核心机制:在抽象类 A 中持有实现接口 B 的引用,通过构造函数由外部传入 B 的具体实现。这个接口 B 就是设计的”桥(Bridge)”
- 解决问题:两个(或多个)独立变化的维度需要自由组合,避免产生子类数量的笛卡尔积爆炸
- 口诀:抽象依赖实现接口,实现通过构造注入
支付渠道(微信/支付宝)× 支付模式(密码/刷脸/指纹)= 如果用继承需要 2×3=6 个子类,
用桥接只需要 2+3=5 个类,新增任何一侧都只加 1 个类。
二、标准体系结构图
classDiagram
class Abstraction {
<<abstract>>
#impl: Implementor
+Abstraction(impl: Implementor)
+operation() void
}
class RefinedAbstraction {
+operation() void
}
class Implementor {
<<interface>>
+operationImpl() void
}
class ConcreteImplementorA {
+operationImpl() void
}
class ConcreteImplementorB {
+operationImpl() void
}
Abstraction <|-- RefinedAbstraction : 继承
Abstraction o--> Implementor : 桥(组合持有)
Implementor <|.. ConcreteImplementorA : 实现
Implementor <|.. ConcreteImplementorB : 实现
| 角色 | 说明 |
|---|---|
| Abstraction(抽象类) | 持有 Implementor 接口引用,定义高层操作,构造函数注入实现 |
| RefinedAbstraction(扩展抽象类) | 继承 Abstraction,扩展具体业务逻辑(如微信支付、支付宝支付) |
| Implementor(实现接口) | 定义实现维度的接口(如支付模式的风控接口) |
| ConcreteImplementor(具体实现) | 实现 Implementor,提供具体行为(如刷脸风控、指纹风控) |
桥就是 Abstraction 中那个 Implementor 类型的成员变量,两个维度通过它连接,可以独立扩展。
三、场景推演
第三方支付聚合平台需要同时支持:
- 支付渠道:微信支付、支付宝支付(未来还会有更多)
- 支付模式:密码支付、刷脸支付、指纹支付(未来还会有更多)
如果用 if-else 硬编码:2 个渠道 × 3 个模式 = 6 种判断分支,全部挤在一个方法里。
如果用继承:WxPayCypher、WxPayFace、WxPayFingerprint、ZfbPayCypher …… 产生 6 个子类,每新增一个渠道或模式,子类数量再次翻倍。
如果用桥接:渠道和模式独立扩展,新增渠道只加 1 个渠道类,新增模式只加 1 个模式类,互不干扰。
四、实战案例
4.1 需求分析
| 维度 | 类型 | 说明 |
|---|---|---|
| 支付渠道 | 微信支付(WxPay) | 划账 + 调用风控接口 |
| 支付渠道 | 支付宝支付(ZfbPay) | 划账 + 调用风控接口 |
| 支付模式 | 密码支付(PayCypher) | 风控:校验环境安全,返回 boolean |
| 支付模式 | 刷脸支付(PayFaceMode) | 风控:校验脸部识别,返回 boolean |
| 支付模式 | 指纹支付(PayFingerprintMode) | 风控:校验指纹信息,返回 boolean |
核心流程:用户发起支付 → 选定渠道 → 对应风控模式安全校验 → 校验通过则划账成功,否则拦截。
4.2 架构图
4.2.1 面条代码架构图

4.2.2 桥接模式架构图

4.3 时序图
4.3.1 面条代码时序图
sequenceDiagram
participant Client as 客户端
participant PC as PayController
Client ->> PC: doPay("weixin_1092033111", "100000109893", 100, 1, 2)
Note over PC: if (channelType == 1) 微信<br/> if (modeType == 2) 人脸<br/> → logger.info("人脸支付,风控校验脸部识别")<br/> → logger.info("模拟微信渠道支付划账开始")
PC -->> Client: true
Client ->> PC: doPay("jlu19dlxo111", "100000109894", 100, 2, 3)
Note over PC: else if (channelType == 2) 支付宝<br/> if (modeType == 3) 指纹<br/> → logger.info("指纹支付,风控校验指纹信息")<br/> → logger.info("模拟支付宝渠道支付划账开始")
PC -->> Client: true
4.3.2 桥接模式时序图
sequenceDiagram
participant Client as 客户端
participant WxPay as WxPay(微信渠道)
participant Face as PayFaceMode(刷脸模式)
participant ZfbPay as ZfbPay(支付宝渠道)
participant Finger as PayFingerprintMode(指纹模式)
Note over Client: 场景一:微信 + 刷脸
Client ->> WxPay: new WxPay(new PayFaceMode())
Client ->> WxPay: transfer("weixin_1092033111", "100000109893", 100)
WxPay ->> WxPay: logger.info("模拟微信渠道支付划账开始")
WxPay ->> Face: security("weixin_1092033111")
Face ->> Face: logger.info("人脸支付,风控校验脸部识别")
Face -->> WxPay: true
WxPay ->> WxPay: logger.info("模拟微信渠道支付风控校验 security:true")
WxPay ->> WxPay: logger.info("模拟微信渠道支付划账成功")
WxPay -->> Client: "0000"
Note over Client: 场景二:支付宝 + 指纹
Client ->> ZfbPay: new ZfbPay(new PayFingerprintMode())
Client ->> ZfbPay: transfer("jlu19dlxo111", "100000109894", 100)
ZfbPay ->> ZfbPay: logger.info("模拟支付宝渠道支付划账开始")
ZfbPay ->> Finger: security("jlu19dlxo111")
Finger ->> Finger: logger.info("指纹支付,风控校验指纹信息")
Finger -->> ZfbPay: true
ZfbPay ->> ZfbPay: logger.info("模拟支付宝渠道支付风控校验 security:true")
ZfbPay ->> ZfbPay: logger.info("模拟支付宝渠道支付划账成功")
ZfbPay -->> Client: "0000"
4.5 代码分析
4.5.1 面条代码(if-else / 硬编码)
1 | // tutorials-10.0-1 PayController.java |
问题清单:
- 支付模式的逻辑在每个渠道下完全重复,渠道 × 模式 = 代码量笛卡尔积
- 新增渠道(如银联)必须再写一套全部模式的逻辑
- 新增模式(如声纹)必须修改每个渠道的代码块
channelType、modeType用魔法数字(1/2/3),可读性差,极易出错- 测试调用:
pay.doPay(..., 1, 2)— 调用方需记住数字含义,接口极不友好
4.5.2 桥接模式代码
(1)实现接口(桥的右侧)— 支付模式
1 | // tutorials-10.0-2 IPayMode.java |
1 | // 密码支付风控 |
每个支付模式只关心自己的风控逻辑,互相独立,可单独测试,新增模式不影响任何渠道代码。
(2)抽象类(桥的左侧)— 支付渠道
1 | // tutorials-10.0-2 Pay.java(核心:持有桥接口的引用) |
Pay不知道也不关心payMode的具体类型,只知道它有security()方法。
这就是”依赖接口而非实现”的体现,也是桥接的精髓。
(3)具体抽象(扩展抽象类)— 各支付渠道
1 | // 微信支付 |
1 | // 支付宝支付(结构与 WxPay 完全对称,渠道日志不同) |
两个渠道类都不包含任何模式判断逻辑,只调用
payMode.security(),风控细节由注入的实现类负责。
(4)客户端调用 — 自由组合
1 | // tutorials-10.0-2 ApiTest.java |
所有可能的组合方式:
| 渠道 \ 模式 | 密码(PayCypher) | 刷脸(PayFaceMode) | 指纹(PayFingerprintMode) |
|---|---|---|---|
| 微信(WxPay) | new WxPay(new PayCypher()) |
new WxPay(new PayFaceMode()) |
new WxPay(new PayFingerprintMode()) |
| 支付宝(ZfbPay) | new ZfbPay(new PayCypher()) |
new ZfbPay(new PayFaceMode()) |
new ZfbPay(new PayFingerprintMode()) |
新增”银联支付”只需新建
UnionPay extends Pay,无需改动任何模式类;
新增”声纹支付”只需新建PayVoiceMode implements IPayMode,无需改动任何渠道类。
总结
桥接模式的核心,是将抽象部分和实现部分分离,让它们可以独立变化。
在本文案例中,支付渠道是一组变化维度,支付模式也是一组变化维度。如果使用 if-else 或单纯继承,每一种渠道和模式的组合都可能产生新的判断分支或子类,最终形成笛卡尔积式的代码膨胀。
桥接模式通过在抽象类中持有实现接口,将“支付渠道”和“支付模式”连接起来。渠道类只负责渠道划账流程,模式类只负责风控校验逻辑,二者通过接口组合使用。这样新增渠道只需要新增渠道类,新增支付模式只需要新增模式类,原有代码无需频繁修改。
因此,桥接模式适合用在存在多个独立变化维度,并且这些维度需要自由组合的场景中。它的用处不只是减少 if-else,更重要的是让系统结构更清晰,职责更单一,扩展更稳定。
桥接模式解决了组合问题,但调用方仍需手动 new WxPay(new PayFaceMode())。可结合抽象工厂模式或策略模式 + Map 配置,将渠道和模式的映射关系配置化(如从配置文件或枚举中读取),实现完全无 if-else 的支付路由。