Java 23 种设计模式:从踩坑到精通 | 番外:状态 vs 策略 ------ if-else 消除的两条路,你走对了吗?
摘要 :状态模式和策略模式都能消除
if-else,都通过多态将不同逻辑封装到独立类中。但它们的核心区别在于谁来决定切换------状态模式中状态对象自己决定下一步流转,策略模式中客户端主动选择算法。本文从订单状态流转和促销策略计算两个实战场景出发,结合代码对比、UML 分析和 Spring State Machine 源码,帮你彻底分清这对"行为型双胞胎"。
🗺️ 本文阅读地图(3 分钟速览)
- 为什么状态和策略都能消除 if-else?它们到底像在哪里?
- 一张表看懂:意图、切换方式、上下文感知的全面对比
- 手写订单状态机 vs 促销策略引擎,感受差异
- Spring State Machine 如何体现代状态模式?
- 面试必问:"状态模式和策略模式怎么区分?能配合使用吗?"
📖 《Java 23 种设计模式:从踩坑到精通》开篇:系列介绍与目录 | 当前:番外 · 状态 vs 策略
🔗 返回系列总目录
文章目录
- [Java 23 种设计模式:从踩坑到精通 | 番外:状态 vs 策略 ------ if-else 消除的两条路,你走对了吗?](#Java 23 种设计模式:从踩坑到精通 | 番外:状态 vs 策略 —— if-else 消除的两条路,你走对了吗?)
-
- [1. 从"订单处理"和"促销计算"两个场景说起](#1. 从“订单处理”和“促销计算”两个场景说起)
-
- [1.1 场景一:订单状态流转(状态模式)](#1.1 场景一:订单状态流转(状态模式))
- [1.2 场景二:促销策略计算(策略模式)](#1.2 场景二:促销策略计算(策略模式))
- [2. UML 对比:结构相似,意图不同](#2. UML 对比:结构相似,意图不同)
-
- [2.1 状态模式 UML](#2.1 状态模式 UML)
- [2.2 策略模式 UML](#2.2 策略模式 UML)
- [3. 三大核心区别](#3. 三大核心区别)
- [4. 深入理解:Spring State Machine 如何体现代状态模式](#4. 深入理解:Spring State Machine 如何体现代状态模式)
- [5. 状态和策略能配合使用吗?](#5. 状态和策略能配合使用吗?)
- [6. 面试必问 + 追问连环炮](#6. 面试必问 + 追问连环炮)
- [🧭 《Java 23 种设计模式:从踩坑到精通》快速导航](#🧭 《Java 23 种设计模式:从踩坑到精通》快速导航)
1. 从"订单处理"和"促销计算"两个场景说起
1.1 场景一:订单状态流转(状态模式)
订单有"待支付"、"已支付"、"已发货"、"已完成"等状态。每个状态下,用户能执行的操作不同。如果用 if-else 判断当前状态再执行操作,代码会迅速膨胀。我们选择状态模式。
java
// 状态模式:状态对象自己决定下一步
public class PaidState implements OrderState {
@Override
public void ship(OrderContext context) {
System.out.println("订单已发货");
context.setState(new ShippedState()); // 状态对象自己决定切换到"已发货"
}
@Override
public void cancel(OrderContext context) {
throw new IllegalStateException("已支付订单无法取消,请申请退款");
}
}
1.2 场景二:促销策略计算(策略模式)
促销有"满减"、"打折"、"立减"等多种算法。用户下单时需要根据促销类型计算价格。我们选择策略模式。
java
// 策略模式:客户端选择策略
Order order = new Order(500);
order.setPromotionStrategy(new FullReductionStrategy()); // 客户端主动选择
double price = order.checkout();
关键差异已浮现:
- 状态模式中,
PaidState自己在ship()方法中调用context.setState()切换到下一个状态,状态对象控制流转。 - 策略模式中,客户端调用
setPromotionStrategy()传入具体策略,客户端控制选择。
2. UML 对比:结构相似,意图不同
2.1 状态模式 UML

2.2 策略模式 UML

UML 的关键差异:
- 状态模式的
Context提供setState()方法,由状态对象在handle()中调用; - 策略模式的
Context提供setStrategy()方法,由客户端调用; - 状态类之间通常有明确的转换关系,策略类之间完全独立。
3. 三大核心区别
| 对比维度 | 状态模式 | 策略模式 |
|---|---|---|
| 意图 | 让对象的行为随内部状态自动改变 | 封装一系列算法,使其可相互替换 |
| 谁决定切换 | 状态对象自己决定何时切换到另一个状态 | 客户端显式选择策略 |
| 上下文感知 | 状态对象持有 Context 引用,可调用 context.setState() |
策略对象通常不知道 Context 的存在 |
| 类间关系 | 状态类之间有明确的转换关系(如待支付→已支付→已发货) | 策略类之间完全独立,互不感知 |
| 典型应用 | 订单状态机、线程状态、工单流转 | 支付方式、排序算法、折扣策略 |
💡 简单记忆:状态模式是"被动流转"------状态对象自己决定下一步;策略模式是"主动选择"------客户端选择算法。
4. 深入理解:Spring State Machine 如何体现代状态模式
Spring State Machine 是一个专门的状态机框架,它通过配置定义状态和事件,由引擎管理状态流转。这本质上就是状态模式的升级版本。
java
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
states
.withStates()
.initial("待支付")
.state("已支付")
.state("已发货")
.end("已完成")
.end("已取消");
}
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
transitions
.withExternal()
.source("待支付").target("已支付").event("支付")
.and()
.withExternal()
.source("待支付").target("已取消").event("取消")
.and()
.withExternal()
.source("已支付").target("已发货").event("发货")
.and()
.withExternal()
.source("已发货").target("已完成").event("确认收货");
}
}
为什么是状态模式?
- 状态的流转由状态机引擎控制(类比状态对象自己决定下一步);
- 每个状态下的合法操作通过配置明确(类比状态对象内部的判断逻辑);
- 外部只需触发事件,无需关心当前处于哪个状态(类比客户端只调用
context.request())。
5. 状态和策略能配合使用吗?
当然可以。 订单状态决定"什么时候该结算",促销策略决定"用什么算法结算"。
java
// 在"已支付"状态中,调用策略模式计算最终价格
public class PaidState implements OrderState {
@Override
public void checkout(OrderContext context, PromotionStrategy strategy) {
double finalPrice = strategy.calculate(context.getOrder());
context.setTotalPrice(finalPrice);
// ... 完成结算,进入下一状态
}
}
💡 组合方式:状态决定"什么时候做",策略决定"怎么做"。两者解耦,各自独立变化。
6. 面试必问 + 追问连环炮
基础必问
- 状态模式和策略模式的区别?
👉 状态模式状态对象自己决定切换(被动流转),策略模式由客户端显式选择(主动替换)。 - JDK 哪里用了状态模式?
👉 线程的状态转换(NEW → RUNNABLE → WAITING → BLOCKED → TERMINATED)。
面试官追问
- "状态模式中状态类为什么要持有上下文引用?"
👉 以便在需要流转时调用context.setState()切换到新状态。策略模式不需要,因为策略只关心算法本身。 - "如果状态转换逻辑很复杂,状态类会膨胀怎么办?"
👉 使用状态机框架(如 Spring State Machine)将状态、事件、转换规则配置化,避免手写大量状态类。 - "状态模式和策略模式能配合使用吗?"
👉 可以。状态决定"何时执行",策略决定"如何执行"。如订单状态决定该结算了,策略决定用满减还是打折。 - "策略模式如何消除 if-else?"
👉 将每个分支算法封装为独立的策略类,通过多态调用。但策略的选择本身可能仍需一个if-else或工厂来根据条件创建具体策略实例------这部分可以交给策略工厂处理。
🎉 恭喜:如果你能立刻说出"状态自己决定流转,策略由客户端选择",并画出状态流转图与策略对比图,你已经掌握了行为型模式中最核心的区分点。
💡 延伸阅读 :本文是状态模式与策略模式的深度对比。如果你想先了解每种模式的完整原理、UML、代码实现和面试题,可以先阅读正篇------状态模式:if-else 满天飞?让状态自己决定行为 和 策略模式:算法族的封装与切换,告别 if-else,再回来看这篇对比,效果更佳。
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 当前:番外 · 状态 vs 策略 ------ if-else 消除的两条路,你走对了吗?(你在这里)
- 📖 延伸阅读:状态模式正篇 | 策略模式正篇
- 创建型模式汇总
- 结构型模式汇总
- 行为型模式汇总
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战 (WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。