设计模式——状态设计模式(行为型)
摘要
状态设计模式是一种行为型设计模式,核心在于允许对象在内部状态改变时改变行为。它通过状态对象封装不同行为,使状态切换灵活清晰。该模式包含环境类、抽象状态类和具体状态类等角色,具有避免大量分支判断、符合单一职责和开闭原则等特点。适用于订单状态管理、流程审批等场景,其结构清晰,实现方式多样,能有效解决状态切换问题。
1. 状态设计模式定义
状态设计模式(State Pattern)是一种行为型设计模式,它的核心思想是:允许对象在其内部状态改变时改变它的行为,使得看起来就像修改了它的类。状态模式允许一个对象在其内部状态发生改变时改变它的行为。这个对象看起来就像修改了它的类一样。
状态模式就像一个“带有状态的自动售货机”——投币、选择商品、出货,每个动作在不同状态下有不同结果。我们通过状态对象来封装不同的行为,让状态切换变得灵活而清晰。
1.1. 🧩 角色组成:
角色 | 说明 |
| 环境类,持有当前状态,定义对外接口,委托状态对象处理行为 |
| 抽象状态接口,定义所有状态的行为方法 |
| 具体状态类,实现不同状态下的行为逻辑,并负责状态切换 |
1.2. ✅ 特点
- 避免了大量
if-else
或switch-case
分支判断 - 每个状态封装一个独立的行为逻辑,符合单一职责原则
- 状态切换内聚在状态对象内部,符合“开闭原则”
1.3. 🧾 示例场景
- 订单状态管理(待支付、已支付、已发货、已完成)
- 流程审批引擎(待审核、审核中、已驳回、已通过)
- 自动售货机、工作流、任务调度状态机等
2. 状态设计模式结构
状态模式包含如下角色:
- Context: 环境类
- State: 抽象状态类
- ConcreteState: 具体状态类
2.1. 状态设计模式类图
2.2. 状态设计模式时序图
3. 状态设计模式实现方式
状态设计模式的实现方式主要依赖对象状态的封装和状态间的转换控制。以下是标准实现方式及其在 Java(或类似面向对象语言)中的常见实现结构。
3.1. 定义抽象状态接口(State
)
public interface State {void handle(Context context);
}
3.2. 定义具体状态类(ConcreteStateA
, ConcreteStateB
)
public class ConcreteStateA implements State {@Overridepublic void handle(Context context) {System.out.println("当前状态:A,处理逻辑中...切换到状态B");context.setState(new ConcreteStateB());}
}public class ConcreteStateB implements State {@Overridepublic void handle(Context context) {System.out.println("当前状态:B,处理逻辑中...切换到状态A");context.setState(new ConcreteStateA());}
}
3.3. 定义上下文(环境类 Context
)
public class Context {private State state;public Context(State state) {this.state = state;}public void setState(State state) {this.state = state;}public void request() {state.handle(this); // 委托当前状态处理}
}
3.4. 客户端调用示例
public class Main {public static void main(String[] args) {Context context = new Context(new ConcreteStateA());context.request(); // 当前状态Acontext.request(); // 切换到Bcontext.request(); // 再切换到A}
}
3.5. 状态设计模式在Spring 或企业项目中的实现方式拓展
在实际项目中,状态模式常结合如下技术使用:
技术栈/机制 | 实现方式说明 |
Spring 容器管理状态类 | 使用注解 |
状态与事件触发分离 | 可结合策略模式或状态机框架(如 Spring Statemachine)来支持更复杂的状态转移图 |
结合枚举做状态标识 | 使用枚举表示状态常量,再映射到状态实现类,便于状态持久化与切换 |
结合数据库存储状态值 | 在业务实体中存储当前状态字段,状态类根据字段值决定是否允许转移 |
3.6. Java 项目中使用状态模式的一些变体实现方式
实现方式 | 说明 |
经典接口实现 | 每个状态一个类,符合设计原则,但类多 |
枚举实现状态 | 用 |
策略+状态融合 | 每个状态类封装为一个策略行为,通过上下文统一调度 |
注解驱动(配合 AOP) | 某些行为切换状态可用注解标注事件,然后由切面完成状态更新 |
总结:状态模式通过将“行为”委托给“状态类”,并在状态类内部控制状态转移,既解耦了状态判断逻辑,也增强了可扩展性和灵活性。
4. 状态设计模式适合场景
4.1. ✅ 适合使用状态模式的场景
场景 | 说明 |
对象状态经常变化 | 如订单、任务、审批流等有多个明确状态,且状态切换频繁、规则复杂。 |
状态行为复杂且相互不同 | 各状态对应的行为差异大,不适合用 if/else 处理。例如支付状态下不能发货,发货状态下不能退款。 |
状态切换有明确流程或图谱 | 如状态转移图能清晰表示状态之间的合法路径,适合模型驱动开发。 |
希望将状态行为局部化 | 避免大量 if-else/switch,在每个状态类中封装对应逻辑,提高代码清晰度。 |
可扩展性要求高 | 新增状态时只需添加一个类,符合开闭原则,不需改动原有逻辑。 |
工作流引擎/状态机系统开发 | 状态流转是核心功能,状态模式天然适配这类系统。 |
4.2. ❌ 不适合使用状态模式的场景
场景 | 原因 |
状态数量很少,逻辑简单 | 如只有 2-3 个状态、行为简单,使用状态类反而增加复杂度。 |
状态行为一致,仅数据不同 | 行为一致可以用策略模式或配置表来处理,无需状态类区分。 |
状态转移规则频繁变化或不稳定 | 状态图不稳定会导致大量状态类频繁调整,维护成本高。 |
只需要一个条件判断即可处理的逻辑 | 强行拆成多个状态类会让代码变啰嗦、不易维护。 |
资源受限的场景(嵌入式、移动端) | 每个状态一个类可能会增加内存开销,不如用状态码枚举和 switch 实现更轻量。 |
4.3. 🧠 总结:
如果你面对的是有限状态机问题(FSM),状态之间行为差异大且需频繁切换,那状态设计模式就是非常合适的选择;反之,则应避免“为了模式而模式”。
5. 状态设计模式实战示例
5.1. ✅ 场景说明:
模拟一个信贷风控审批流程,审批任务的状态有以下几种:
待初审
→初审通过
→复审通过
→审批完成
- 各个状态下行为不同,如“提交”、“退回”、“终止”等
5.2. ✅ 类结构图(State 模式组成)
[State] ← 抽象接口↑ ↑ ↑
[AState] [BState] [CState] ← 具体状态类[ApprovalContext] ← 上下文类,包含状态对象 + 状态切换逻辑
5.3. 🛠 抽象状态接口
public interface ApprovalState {void submit(ApprovalContext context);void reject(ApprovalContext context);String getStateCode(); // 标识状态
}
5.4. 🛠 上下文类(使用 Spring 注解注入状态对象)
@Component
public class ApprovalContext {// 所有状态实现类注入到 Map,key 为状态 code@Autowiredprivate List<ApprovalState> stateList;private Map<String, ApprovalState> stateMap;private ApprovalState currentState;@PostConstructpublic void init() {stateMap = stateList.stream().collect(Collectors.toMap(ApprovalState::getStateCode, s -> s));}public void setCurrentState(String stateCode) {this.currentState = stateMap.get(stateCode);}public void submit() {currentState.submit(this);}public void reject() {currentState.reject(this);}public String getCurrentStateCode() {return currentState.getStateCode();}
}
5.5. 🛠 具体状态实现类(以“待初审”为例)
@Component
public class WaitInitialApprovalState implements ApprovalState {@Overridepublic void submit(ApprovalContext context) {System.out.println("当前状态:待初审 → 执行提交 → 切换到初审通过");context.setCurrentState("APPROVED_INIT");}@Overridepublic void reject(ApprovalContext context) {System.out.println("当前状态:待初审 → 执行驳回 → 切换到终止状态");context.setCurrentState("TERMINATED");}@Overridepublic String getStateCode() {return "WAIT_INIT";}
}
再如 “初审通过” 状态:
@Component
public class ApprovedInitialState implements ApprovalState {@Overridepublic void submit(ApprovalContext context) {System.out.println("当前状态:初审通过 → 执行提交 → 切换到复审通过");context.setCurrentState("APPROVED_FINAL");}@Overridepublic void reject(ApprovalContext context) {System.out.println("当前状态:初审通过 → 驳回 → 回到初审");context.setCurrentState("WAIT_INIT");}@Overridepublic String getStateCode() {return "APPROVED_INIT";}
}
5.6. 🧪 控制层模拟调用
@RestController
@RequestMapping("/approval")
public class ApprovalController {@Autowiredprivate ApprovalContext context;@GetMapping("/start")public String start() {context.setCurrentState("WAIT_INIT");return "流程已启动,当前状态:" + context.getCurrentStateCode();}@PostMapping("/submit")public String submit() {context.submit();return "提交后,当前状态:" + context.getCurrentStateCode();}@PostMapping("/reject")public String reject() {context.reject();return "驳回后,当前状态:" + context.getCurrentStateCode();}
}
5.7. 🧠 技术亮点
点 | 说明 |
✅ Spring 注解注入 | 使用 |
✅ Map 管理状态 | 使用 |
✅ 避免构造注入 | 通过字段注入 |
✅ 易于扩展 | 添加新状态只需新增一个实现类,无需修改原有逻辑,符合开闭原则 |
5.8. ✅ 总结:
本示例将状态设计模式与 Spring 注解机制结合,实现了一个灵活、可扩展的金融风控审批流程状态机,适合真实风控系统中审批流、处理流的状态管理需求。
6. 状态设计模式思考
6.1. 状态设计模式与状态机设计有什么关系?
状态设计模式(State Pattern)与状态机(State Machine)有密切关系,但两者侧重点不同。理解它们的联系与区别有助于你在项目中正确选择使用方式,特别是在风控、审批流等系统中。
6.1.1. ✅ 二者关系概述:
项目 | 状态设计模式(State Pattern) | 状态机(State Machine) |
核心概念 | 将状态行为封装为类,行为由当前状态对象决定 | 明确的状态集合、事件集合、转移规则 |
关注点 | 封装状态行为和状态切换逻辑(面向对象) | 管理状态 + 事件 + 转移图谱(面向模型) |
表达能力 | 表达某对象在不同状态下行为不同 | 能描述复杂的状态流、事件触发与状态转移 |
实现方式 | 使用状态类、上下文对象,代码驱动 | 可以是代码、配置、状态转移图、框架等 |
适合场景 | 状态数有限,行为差异大,关注行为封装 | 状态众多,转移复杂,关注状态流和路径 |
是否一定使用类 | ✅ 每个状态类实现接口 | ❌ 可纯配置(如状态转移表、DSL) |
常见代表 | 状态模式(GoF) | 有限状态机(FSM)、Spring Statemachine |
6.1.2. ✅ 类比举例:审批流程
- 状态设计模式:
你创建WaitApprovalState
、ApprovedState
、RejectedState
等类,在类中定义“提交”、“驳回”等行为。 - 状态机模型:
你建一个状态图:
WAIT_APPROVAL
--(submit)-->APPROVED
WAIT_APPROVAL
--(reject)-->REJECTED
并用框架(如 Spring Statemachine)实现。
6.1.3. ✅ 状态设计模式 vs 状态机的总结图解:
┌─────────────────────┐│ 状态设计模式 ││ 封装状态行为 ││ 各状态一个类 │└────────┬────────────┘│▼状态逻辑复杂,可扩展性强│▼┌─────────────────────┐│ 状态机模型 ││ 管理状态转移路径 ││ 可视化或配置驱动 │└─────────────────────┘
6.2. ✅ 实践建议
情况 | 推荐方案 |
状态较少、行为差异大 | 使用状态设计模式,可读性强、便于扩展 |
状态较多、转移复杂、事件驱动 | 使用状态机模型(框架),如 Spring Statemachine |
想表达清晰状态流 | 先画出状态图,用状态机模型来支撑业务流程设计 |
6.3. 🧠 总结:
状态设计模式是状态机的一种实现方式,适用于行为封装;而状态机更偏向建模工具,适用于流程表达和自动化管理。两者可以结合使用,如状态类 + 状态图配置来实现灵活状态系统。
博文参考
- 4. 状态模式 — Graphic Design Patterns
- 状态设计模式
- 设计模式之状态模式 | DESIGN