设计模式(二十一)行为型:状态模式详解
设计模式(二十一)行为型:状态模式详解
状态模式(State Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于允许一个对象在其内部状态改变时改变其行为,使得对象看起来像是修改了它的类。它通过将与特定状态相关的行为封装到独立的状态类中,将庞大的条件分支(如
if-else
或switch-case
)转化为对象间的委托关系,从而实现行为的动态切换与高度可扩展性。状态模式是构建复杂状态机、工作流引擎、游戏角色行为、网络连接管理、订单生命周期、UI 状态管理等系统的理想选择,是将“状态驱动行为”这一自然逻辑优雅映射到面向对象设计的关键范式。
一、详细介绍
状态模式解决的是“一个对象的行为取决于其内部状态,且状态数量较多、状态转换复杂、行为随状态频繁变化”的问题。在传统设计中,通常使用条件语句(如 switch
)根据当前状态决定执行何种行为。这导致:
- 代码臃肿:所有状态相关的行为集中在单一类中,方法庞大。
- 难以维护:新增状态或修改状态转换逻辑需要修改大量
switch
语句。 - 违反单一职责原则:一个类承担了所有状态的行为。
- 违反开闭原则:对扩展开放,对修改关闭。
状态模式的核心思想是:将每个状态封装成一个独立的类,每个状态类实现与该状态相关的行为。原对象(上下文)持有当前状态对象的引用,并将状态相关的行为委托给当前状态对象执行。
该模式包含以下核心角色:
- Context(上下文):定义客户端使用的接口,维护一个对
State
对象的引用,表示当前状态。它将状态相关的行为委托给当前状态对象。 - State(状态接口):定义所有具体状态类共有的接口,声明与状态相关的行为方法(如
handle()
)。 - ConcreteStateA, ConcreteStateB, …(具体状态类):实现
State
接口,封装与特定状态相关的行为。每个具体状态类知道在特定操作下应如何响应,并可能在响应后改变上下文的当前状态(即状态转换)。
状态模式的关键优势:
- 消除复杂条件语句:将
switch
逻辑转化为多态方法调用。 - 符合开闭原则:新增状态只需添加新的具体状态类,无需修改现有代码。
- 符合单一职责原则:每个状态类只负责一种状态的行为。
- 提高可维护性:状态行为集中,逻辑清晰。
- 支持动态状态转换:状态转换由状态对象内部决定或由上下文协调。
与“策略模式”相比,策略模式关注算法的替换,状态模式关注状态驱动的行为变化;策略通常由客户端选择,状态由内部逻辑自动转换。与“观察者模式”相比,观察者是一对多通知,状态是单对象行为切换。与“命令模式”相比,命令封装请求,状态封装行为。
状态模式适用于:
- 对象有明确的状态概念(如订单:新建、已支付、已发货、已完成)。
- 行为随状态变化而变化。
- 状态转换逻辑复杂。
- 需要避免庞大的条件语句。
二、状态模式的UML表示
以下是状态模式的标准 UML 类图:
图解说明:
Context
持有对State
的引用,代表当前状态。Context
的request()
方法将调用委托给当前State
的handle()
。ConcreteState
实现handle()
,执行特定于该状态的行为,并可能调用Context
的setState()
来触发状态转换。- 状态转换可以由状态对象自身决定,或由
Context
根据业务逻辑协调。
三、一个简单的Java程序实例及其UML图
以下是一个 TCP 连接状态机的简化示例,包含 CLOSED
, LISTEN
, ESTABLISHED
, CLOSE_WAIT
状态。
Java 程序实例
// 状态接口
interface TCPState {void activeOpen(TCPConnection context);void passiveOpen(TCPConnection context);void close(TCPConnection context);void acknowledge(TCPConnection context);void send(TCPConnection context);String getStateName(); // 用于显示
}// 上下文:TCP连接
class TCPConnection {private TCPState currentState;// 预定义状态实例(可单例)private static final TCPState CLOSED = new ClosedState();private static final TCPState LISTEN = new ListenState();private static final TCPState ESTABLISHED = new EstablishedState();private static final TCPState CLOSE_WAIT = new CloseWaitState();public TCPConnection() {this.currentState = CLOSED;System.out.println("🔌 连接初始化为 CLOSED 状态");}// 状态相关操作,委托给当前状态public void activeOpen() {System.out.println("👉 客户端发起主动打开...");currentState.activeOpen(this);}public void passiveOpen() {System.out.println("👉 服务端发起被动打开...");currentState.passiveOpen(this);}public void close() {System.out.println("👉 发起关闭连接...");currentState.close(this);}public void acknowledge() {System.out.println("👉 收到确认...");currentState.acknowledge(this);}public void send() {System.out.println("👉 发送数据...");currentState.send(this);}// 状态转换方法public void changeState(TCPState newState) {if (this.currentState != null) {System.out.println("🔄 状态转换: " + this.currentState.getStateName() + " → " + newState.getStateName());}this.currentState = newState;}// 获取当前状态(用于状态判断,可选)public TCPState getCurrentState() {return currentState;}// 预定义状态的获取方法(简化客户端使用)public static TCPState getClosedState() { return CLOSED; }public static TCPState getListenState() { return LISTEN; }public static TCPState getEstablishedState() { return ESTABLISHED; }public static TCPState getCloseWaitState() { return CLOSE_WAIT; }
}// 具体状态:CLOSED
class ClosedState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println(" ✅ CLOSED: 执行主动打开 -> 发送 SYN, 进入 SYN_SENT (本例简化为直接进入 ESTABLISHED)");context.changeState(TCPConnection.getEstablishedState());}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println(" ✅ CLOSED: 执行被动打开 -> 进入 LISTEN");context.changeState(TCPConnection.getListenState());}@Overridepublic void close(TCPConnection context) {System.out.println(" ⚠️ CLOSED: 连接已关闭,无需操作");}@Overridepublic void acknowledge(TCPConnection context) {System.out.println(" ❌ CLOSED: 无法处理确认");}@Overridepublic void send(TCPConnection context) {System.out.println(" ❌ CLOSED: 连接未建立,无法发送");}@Overridepublic String getStateName() {return "CLOSED";}
}// 具体状态:LISTEN
class ListenState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println(" ✅ LISTEN: 收到 SYN -> 发送 SYN-ACK, 进入 SYN_RECEIVED (本例简化为进入 ESTABLISHED)");context.changeState(TCPConnection.getEstablishedState());}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println(" ⚠️ LISTEN: 已在监听状态");}@Overridepublic void close(TCPConnection context) {System.out.println(" ✅ LISTEN: 关闭监听 -> 进入 CLOSED");context.changeState(TCPConnection.getClosedState());}@Overridepublic void acknowledge(TCPConnection context) {System.out.println(" ❌ LISTEN: 未建立连接,无法确认");}@Overridepublic void send(TCPConnection context) {System.out.println(" ❌ LISTEN: 连接未建立,无法发送");}@Overridepublic String getStateName() {return "LISTEN";}
}// 具体状态:ESTABLISHED
class EstablishedState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println(" ⚠️ ESTABLISHED: 连接已建立,无需打开");}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println(" ⚠️ ESTABLISHED: 连接已建立,无需打开");}@Overridepublic void close(TCPConnection context) {System.out.println(" ✅ ESTABLISHED: 发送 FIN -> 进入 FIN_WAIT_1 (本例简化为进入 CLOSE_WAIT)");context.changeState(TCPConnection.getCloseWaitState());}@Overridepublic void acknowledge(TCPConnection context) {System.out.println(" ✅ ESTABLISHED: 确认数据包");// 通常发送 ACK}@Overridepublic void send(TCPConnection context) {System.out.println(" ✅ ESTABLISHED: 数据发送成功");// 发送数据包}@Overridepublic String getStateName() {return "ESTABLISHED";}
}// 具体状态:CLOSE_WAIT
class CloseWaitState implements TCPState {@Overridepublic void activeOpen(TCPConnection context) {System.out.println(" ❌ CLOSE_WAIT: 连接正在关闭,无法打开");}@Overridepublic void passiveOpen(TCPConnection context) {System.out.println(" ❌ CLOSE_WAIT: 连接正在关闭,无法打开");}@Overridepublic void close(TCPConnection context) {System.out.println(" ✅ CLOSE_WAIT: 发送 FIN -> 进入 LAST_ACK (本例简化为进入 CLOSED)");context.changeState(TCPConnection.getClosedState());}@Overridepublic void acknowledge(TCPConnection context) {System.out.println(" ✅ CLOSE_WAIT: 确认对方的 FIN");}@Overridepublic void send(TCPConnection context) {System.out.println(" ⚠️ CLOSE_WAIT: 可能允许发送最后数据,但本例禁止");}@Overridepublic String getStateName() {return "CLOSE_WAIT";}
}// 客户端使用示例
public class StatePatternDemo {public static void main(String[] args) {System.out.println("🌐 TCP 连接状态机 - 状态模式示例\n");TCPConnection connection = new TCPConnection();System.out.println("\n--- 客户端连接流程 ---");connection.activeOpen(); // CLOSED -> ESTABLISHEDconnection.send(); // 发送数据connection.acknowledge(); // 确认connection.close(); // ESTABLISHED -> CLOSE_WAIT -> CLOSEDSystem.out.println("\n--- 服务端连接流程 ---");connection.passiveOpen(); // CLOSED -> LISTENconnection.activeOpen(); // LISTEN -> ESTABLISHED (收到客户端 SYN)connection.send(); // 发送数据connection.close(); // ESTABLISHED -> CLOSE_WAIT -> CLOSEDSystem.out.println("\n--- 在 CLOSED 状态尝试无效操作 ---");connection.send(); // 应提示无法发送}
}
实例对应的UML图(简化版)
运行说明:
TCPConnection
是上下文,持有当前TCPState
。- 每个操作(
activeOpen
,close
等)被委托给当前状态对象的对应方法。 - 状态方法执行特定行为,并可能调用
changeState()
触发状态转换。 - 不同状态对同一操作的响应不同(如
CLOSED
和ESTABLISHED
对send
的处理)。 - 状态转换逻辑内置于状态类中,清晰且可扩展。
四、总结
特性 | 说明 |
---|---|
核心目的 | 允许对象在状态改变时改变行为,消除条件分支 |
实现机制 | 将状态行为封装到独立类,上下文委托调用 |
优点 | 消除复杂条件语句、符合开闭/单一职责原则、提高可维护性、支持动态转换 |
缺点 | 增加类数量、状态转换逻辑可能分散、简单状态机可能过度设计 |
适用场景 | 复杂状态机、工作流、订单生命周期、游戏AI、UI状态管理 |
不适用场景 | 状态极少、行为简单、状态转换固定 |
状态模式使用建议:
- 状态类可设计为单例(无状态或共享状态)。
- 状态转换可由状态对象自身决定,或由上下文根据业务规则协调。
- 可结合“工厂模式”创建状态对象。
- 在 Java 中,
enum
可实现简单状态模式(每个枚举常量实现接口)。
架构师洞见:
状态模式是“有限状态机(FSM)”在面向对象中的优雅实现。在现代架构中,其思想已演变为工作流引擎(如 Camunda, Airflow)、事件驱动状态管理(Redux, Vuex)、服务编排(Kubernetes Operators) 和 AI Agent 的决策状态 的核心。例如,Redux 的reducer
函数根据action
和当前state
计算新state
,本质是状态模式的函数式变体;微服务的 Saga 模式管理分布式事务状态;在自动驾驶中,车辆行为模式(巡航、变道、停车)是复杂的状态机。未来趋势是:状态模式将与形式化方法结合,实现状态机的自动验证;在量子计算中,量子态的演化可建模为状态转换;在元宇宙中,虚拟角色的行为状态(行走、战斗、交互)由状态模式驱动;在AI中,LLM Agent 的“思考-行动-观察”循环可视为一个高级状态机。
掌握状态模式,是设计复杂业务逻辑、高可靠性系统的关键。作为架构师,应在面对“多状态、多行为、复杂转换”的领域模型时,果断采用状态模式。它不仅是模式,更是系统确定性的保障——它将混沌的条件逻辑转化为清晰、可验证、可演进的状态图,让系统的每一次行为变迁都变得可预测、可追溯、可管理,从而构建出真正健壮、可信赖的软件系统。