Java 23 种设计模式:从踩坑到精通 | 番外:策略模式 —— 电商支付方式切换实战
Java 23 种设计模式:从踩坑到精通 | 番外:策略模式 —— 电商支付方式切换实战
摘要:策略模式定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端,通过“组合 + 多态”彻底消除
if-else条件判断。本文结合电商支付方式切换的场景,完整展示如何用策略模式动态切换支付宝、微信等支付渠道,并与状态模式深度对比,帮你掌握“算法对象化”的设计精髓。
🗺️ 本文阅读地图(3 分钟速览)
- 为什么支付方式不能写死在订单类里?
- 策略模式核心角色:抽象策略、具体策略、上下文
- 手写支付策略:支付宝、微信动态切换
- Lambda 简化:逻辑简单时不用写类
- 面试必问:“策略模式和状态模式有什么区别?
Comparator用了哪个?”
📖 《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:Strategy 策略模式—— 算法族的封装与切换,告别 if-else | 当前:番外 · 策略模式 × 电商支付方式切换
🔗 返回系列总目录
1. 电商支付方式的痛点
在电商系统中,订单支付支持多种方式——支付宝、微信、银联等。如果直接在订单类中根据支付方式写 if-else:
void pay(Order order, String type) {
if ("alipay".equals(type)) {
// 调用支付宝接口
} else if ("wechat".equals(type)) {
// 调用微信接口
} else if ("unionpay".equals(type)) {
// 调用银联接口
}
// 新增支付方式要改这里...
}
这种写法将订单与具体支付渠道紧耦合,新增一种支付方式(如抖音支付)就要修改订单类。更麻烦的是,如果用户在下单后想切换支付方式,静态 if-else 无法优雅支持。
策略模式的解决思路:将每种支付方式封装为独立的策略类,订单类(上下文)只持有抽象策略引用,通过 setStrategy() 在运行时动态切换。新增支付方式只需新增一个策略类,订单代码零修改。
1.1 你的场景该不该用策略模式?
| 判断标准 | 是 → 用策略模式 | 否 → 用其他方式 |
|---|---|---|
| 同一个操作有多种实现方式,且可能动态切换 | ✅ | ❌ |
| 算法逻辑复杂,相互独立 | ✅ | ❌ |
| 未来需要增加新算法,不希望修改原有代码 | ✅ | ❌ |
| 只有两三种固定不变的简单分支 | ❌ | 直接用 if-else 即可 |
2. 策略模式 UML(电商支付方式场景)

3. 完整源码实现
3.1 抽象策略接口 (Strategy)
/**
* 抽象策略:定义支付算法的公共接口
*/
public interface Strategy {
void algorithm();
}
💬 白话:所有支付方式都必须实现
algorithm()——不管支付宝还是微信,最终都是“执行一个支付动作”。
3.2 具体策略A:支付宝支付 (ConcreteStrategyA)
/**
* 具体策略A:支付宝支付
*/
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithm() {
System.out.println("💳 正在调用【支付宝】支付接口...");
System.out.println(" 🔗 跳转支付宝收银台...");
System.out.println(" ✅ 支付宝支付成功!");
}
}
3.3 具体策略B:微信支付 (ConcreteStrategyB)
/**
* 具体策略B:微信支付
*/
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithm() {
System.out.println("💳 正在调用【微信支付】接口...");
System.out.println(" 📱 弹出微信付款码...");
System.out.println(" ✅ 微信支付成功!");
}
}
💬 白话:两个策略类实现完全相同的接口,但内部逻辑各自独立。新增银联支付只需新增一个
ConcreteStrategyC,支付宝和微信的代码完全不用动。
3.4 上下文:订单支付入口 (Context)
/**
* 上下文:持有策略引用,对外提供统一调用入口
*/
public class Context {
private Strategy strategy;
/** 构造时注入策略(推荐方式,避免空指针) */
public Context(Strategy strategy) {
this.strategy = strategy;
}
/** 动态切换策略 */
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
/** 执行当前策略的算法 */
public void executeStrategy() {
if (strategy == null) {
throw new IllegalStateException("未设置支付策略!");
}
strategy.algorithm();
}
}
💬 白话:上下文就是一个“支付入口”——它不关心用的是支付宝还是微信,只知道自己持有一个策略,执行时调用策略的
algorithm()。切换支付方式只需setStrategy(),一行代码搞定。
3.5 客户端测试
public class Client {
public static void main(String[] args) {
// 场景1:用户选择支付宝支付
System.out.println("=== 订单 #2024001 ===");
Context context = new Context(new ConcreteStrategyA());
context.executeStrategy();
System.out.println();
// 场景2:运行时动态切换为微信支付
System.out.println("=== 订单 #2024002 ===");
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();
}
}
4. 运行结果
=== 订单 #2024001 ===
💳 正在调用【支付宝】支付接口...
🔗 跳转支付宝收银台...
✅ 支付宝支付成功!
=== 订单 #2024002 ===
💳 正在调用【微信支付】接口...
📱 弹出微信付款码...
✅ 微信支付成功!
5. 核心角色回顾
| 角色 | 职责 | 对应代码 |
|---|---|---|
| Strategy | 定义所有支持算法的公共接口 | Strategy |
| ConcreteStrategy | 实现具体算法,每种策略独立 | ConcreteStrategyA/B |
| Context | 持有策略引用,提供统一调用入口 | Context |
6. Lambda 简化策略模式(Java 8+)
如果策略接口只有一个抽象方法(函数式接口),可以用 Lambda 表达式直接内联,避免为每种策略建一个类:
// 直接用 Lambda 替代具体策略类
Context alipay = new Context(() -> {
System.out.println("💳 正在调用【支付宝】支付接口...");
});
alipay.executeStrategy();
// 微信支付
Context wechat = new Context(() -> {
System.out.println("💳 正在调用【微信支付】接口...");
});
wechat.executeStrategy();
💬 白话:支付逻辑只有几行时,不需要专门写
ConcreteStrategyA/ConcreteStrategyB类。Lambda 直接内联,代码更简洁。但如果策略逻辑复杂(如包含多步骤状态流转),独立类更清晰。
7. 策略模式 vs 状态模式
这是面试中极容易混淆的一对:
| 对比项 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 封装算法族,使其可替换 | 行为随内部状态自动改变 |
| 谁触发切换 | 客户端显式调用 setStrategy() |
状态对象自己调用 context.setState() |
| 上下文感知 | 策略类通常不知道上下文的存在 | 状态类持有并操作上下文引用 |
| 典型应用 | 支付方式、排序算法、促销策略 | 订单状态机、JDK 线程状态 |
💡 一句话记忆:策略模式是“主动替换”——客户端选择用哪个算法;状态模式是“被动流转”——状态自己决定下一步去哪。本例中支付方式的切换由客户端调用
setStrategy()触发,是典型的策略模式。
8. 策略模式的优缺点
| 优点 | 缺点 |
|---|---|
| 消除 if-else,用多态替代条件判断 | 策略类数量增多 |
| 运行时动态切换策略 | 客户端需了解各策略的特点才能正确选择 |
| 新增策略无需修改上下文,符合开闭原则 | 策略间通信成本较高 |
| Lambda 可进一步简化简单策略 | 过度设计风险:2~3 种固定分支用 if-else 即可 |
9. 六大设计原则体现
| 原则 | 体现 |
|---|---|
| 单一职责 | 每种策略只负责一种算法实现 |
| 开闭原则 | 新增支付方式无需修改 Context |
| 里氏替换 | 所有策略可替换 Strategy 接口 |
| 依赖倒置 | Context 依赖抽象 Strategy 接口 |
| 接口隔离 | Strategy 只有 algorithm() 一个方法 |
| 迪米特法则 | Context 只知道策略接口,不知内部实现 |
附 策略模式 UML源码(电商支付方式场景)
@startuml
title Java 23 种设计模式:从踩坑到精通
footer 折哥 | 智能物流与Java实战
' 1. 全局样式配置
skinparam backgroundColor #FEFEFE
skinparam shadowing false
skinparam classBorderColor #333333
skinparam classFontColor #1A1A1A
skinparam classFontSize 14
skinparam noteFontSize 12
skinparam noteFontColor #555555
skinparam arrowColor #555555
skinparam classBackgroundColor #F9F9F9
skinparam interface {
BackgroundColor #E8F5E9
BorderColor #2E7D32
}
' 2. 抽象策略
interface Strategy {
+ algorithm()
}
note right of Strategy
<b>抽象策略</b>
--
定义所有支持算法的公共接口
Context 通过此接口调用具体算法
end note
' 3. 具体策略A
class ConcreteStrategyA implements Strategy {
+ algorithm()
}
note right of ConcreteStrategyA
<b>具体策略A</b>
--
实现算法A
例如:支付宝支付
end note
' 4. 具体策略B
class ConcreteStrategyB implements Strategy {
+ algorithm()
}
note right of ConcreteStrategyB
<b>具体策略B</b>
--
实现算法B
例如:微信支付
end note
' 5. 上下文
class Context {
- strategy : Strategy
+ setStrategy(Strategy)
+ executeStrategy()
}
note right of Context
<b>上下文</b>
--
持有一个 Strategy 引用
可动态切换策略
屏蔽具体策略的实现细节
end note
' 6. 关系连线
Context o-down-> Strategy : 持有
ConcreteStrategyA .up.> Strategy : 实现
ConcreteStrategyB .up.> Strategy : 实现
@enduml
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 正篇:Strategy 策略模式 —— 算法族的封装与切换,告别 if-else
- 当前:番外 · 策略模式 × 电商支付方式切换(你在这里)
- 创建型模式汇总
- 结构型模式汇总
- 行为型模式汇总
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。
更多推荐





所有评论(0)