深入理解设计模式之状态模式(State Pattern)
前言
在软件开发中,我们经常会遇到对象的行为依赖于其内部状态的场景。传统的做法是使用大量的if-else或switch-case语句来判断状态,这不仅让代码变得臃肿难维护,还违反了开闭原则。状态模式(State Pattern)正是为解决这类问题而设计的行为型模式。
本文将深入探讨状态模式的原理、实现方式,并结合订单系统、工作流引擎等实际生产场景,以及Spring State Machine等开源框架的应用,帮助读者全面掌握这一重要的设计模式。
一、什么是状态模式
1.1 定义
状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变其行为,看起来就像是改变了对象的类。状态模式将与特定状态相关的行为封装到独立的状态类中,使得对象在不同状态下具有不同的行为。
1.2 核心思想
状态模式的核心思想是:
- 将状态的判断逻辑转移到表示不同状态的类中
- 可以让对象的内部状态改变时改变其行为
- 状态转换的规则封装在状态类内部
1.3 状态模式的结构
+------------------+
| Context |
+------------------+
| - state: State |
+------------------+
| + request() |
| + setState() |
+------------------+
|
| 持有
↓
+------------------+
| <<interface>> |
| State |
+------------------+
| + handle() |
+------------------+
△
|
+-----------+-----------+
| | |
+-----------+ +-----------+ +-----------+
|ConcreteA | |ConcreteB | |ConcreteC |
|State | |State | |State |
+-----------+ +-----------+ +-----------+
|+ handle() | |+ handle() | |+ handle() |
+-----------+ +-----------+ +-----------+
1.4 状态模式的角色
-
Context(环境类):定义客户端感兴趣的接口,维护一个具体状态类的实例,这个实例定义当前状态。
-
State(抽象状态类):定义一个接口,用于封装与Context的一个特定状态相关的行为。
-
ConcreteState(具体状态类):实现抽象状态类中定义的接口,负责处理Context在该状态下的行为。
二、为什么需要状态模式
2.1 传统方式的问题
让我们先看一个不使用状态模式的订单处理示例:
/**
* 传统方式:使用if-else处理状态
*/
public class OrderTraditional {
private static final int STATE_NEW = 1;
private static final int STATE_PAID = 2;
private static final int STATE_SHIPPED = 3;
private static final int STATE_DELIVERED = 4;
private static final int STATE_CANCELLED = 5;
private int state = STATE_NEW;
public void pay() {
if (state == STATE_NEW) {
System.out.println("订单已支付");
state = STATE_PAID;
} else if (state == STATE_PAID) {
System.out.println("订单已经支付过了");
} else if (state == STATE_CANCELLED) {
System.out.println("订单已取消,无法支付");
} else {
System.out.println("当前状态无法支付");
}
}
public void ship() {
if (state == STATE_PAID) {
System.out.println("订单已发货");
state = STATE_SHIPPED;
} else if (state == STATE_NEW) {
System.out.println("订单未支付,无法发货");
} else if (state == STATE_SHIPPED) {
System.out.println("订单已经发货了");
} else {
System.out.println("当前状态无法发货");
}
}
public void cancel() {
if (state == STATE_NEW || state == STATE_PAID) {
System.out.println("订单已取消");
state = STATE_CANCELLED;
} else {
System.out.println("当前状态无法取消");
}
}
// 更多方法...每个方法都充斥着if-else判断
}
传统方式的问题:
- 代码臃肿,充斥着大量的条件判断
- 难以扩展,增加新状态需要修改多处代码
- 违反开闭原则和单一职责原则
- 状态转换逻辑分散,难以维护
2.2 状态模式的优势
- 封装性良好:将状态相关的行为封装到独立的类中
- 易于扩展:增加新状态只需添加新的状态类
- 消除条件语句:用多态替代条件判断
- 清晰的状态转换:状态转换规则集中管理
- 符合开闭原则:对扩展开放,对修改关闭
三、状态模式的实现
3.1 基础实现示例
让我们使用状态模式重构订单处理系统:
/**
* 抽象状态接口
*/
interface OrderState {
void pay(OrderContext context);
void ship(OrderContext context);
void deliver(OrderContext context);
void cancel(OrderContext context);
String getStateName();
}
/**
* 环境类:订单上下文
*/
class OrderContext {
private OrderState currentState;
private String orderId;
public OrderContext(String orderId) {
this.orderId = orderId;
// 初始状态为新建状态
this.currentState = new NewOrderState();
System.out.println("订单创建: " + orderId);
}
public void setState(OrderState state) {
System.out.println("订单状态变更: " + currentState.getStateName() +
" -> " + state.getStateName());
this.currentState = state;
}
public String getOrderId() {
return orderId;
}
// 委托给当前状态处理
public void pay() {
currentState.pay(this);
}
public void ship() {
currentState.ship(this);
}
public void deliver() {
currentState.deliver(this);
}
public void cancel() {
currentState.cancel(this);
}
public String getCurrentState() {
return currentState.getStateName();
}
}
/**
* 具体状态类:新建状态
*/
class NewOrderState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("订单支付成功");
context.setState(new PaidOrderState());
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:订单未支付,无法发货");
}
@Override
public void deliver(OrderContext context) {
System.out.println("错误:订单未发货,无法确认收货");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消");
context.setState(new CancelledOrderState());
}
@Override
public String getStateName() {
return "新建";
}
}
/**
* 具体状态类:已支付状态
*/
class PaidOrderState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("错误:订单已支付,无法重复支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("订单已发货,物流单号: SF" + System.currentTimeMillis());
context.setState(new ShippedOrderState());
}
@Override
public void deliver(OrderContext context) {
System.out.println("错误:订单未发货,无法确认收货");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消,退款将在3-5个工作日内到账");
context.setState(new CancelledOrderState());
}
@Override
public String getStateName() {
return "已支付";
}
}
/**
* 具体状态类:已发货状态
*/
class ShippedOrderState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("错误:订单已支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:订单已发货");
}
@Override
public void deliver(OrderContext context) {
System.out.println("确认收货成功,感谢您的购买");
context.setState(new DeliveredOrderState());
}
@Override
public void cancel(OrderContext context) {
System.out.println("错误:订单已发货,如需退货请联系客服");
}
@Override
public String getStateName() {
return "已发货";
}
}
/**
* 具体状态类:已送达状态
*/
class DeliveredOrderState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("错误:订单已完成");
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:订单已完成");
}
@Override
public void deliver(OrderContext context) {
System.out.println("订单已完成");
}
@Override
public void cancel(OrderContext context) {
System.out.println("错误:订单已完成,如需退货请联系客服");
}
@Override
public String getStateName() {
return "已送达";
}
}
/**
* 具体状态类:已取消状态
*/
class CancelledOrderState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("错误:订单已取消,无法支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("错误:订单已取消,无法发货");
}
@Override
public void deliver(OrderContext context) {
System.out.println("错误:订单已取消");
}
@Override
public void cancel(OrderContext context) {
System.out.println("订单已取消");
}
@Override
public String getStateName() {
return "已取消";
}
}
/**
* 客户端测试
*/
public class StatePatternDemo {
public static void main(String[] args) {
// 创建订单
OrderContext order = new OrderContext("ORDER_20231119001");
System.out.println("当前状态: " + order.getCurrentState() + "\n");
// 正常流程
order.pay();
System.out.println("当前状态: " + order.getCurrentState() + "\n");
order.ship();
System.out.println("当前状态: " + order.getCurrentState() + "\n");
order.deliver();
System.out.println("当前状态: " + order.getCurrentState() + "\n");
// 尝试非法操作
order.cancel();
System.out.println("\n--- 异常流程测试 ---");
// 测试异常流程
OrderContext order2 = new OrderContext("ORDER_20231119002");
order2.ship(); // 未支付就发货
order2.pay();
order2.cancel(); // 支付后取消
}
}
输出结果:
订单创建: ORDER_20231119001
当前状态: 新建
订单支付成功
订单状态变更: 新建 -> 已支付
当前状态: 已支付
订单已发货,物流单号: SF1700380800123
订单状态变更: 已支付 -> 已发货
当前状态: 已发货
确认收货成功,感谢您的购买
订单状态变更: 已发货 -> 已送达
当前状态: 已送达
错误:订单已完成,如需退货请联系客服
--- 异常流程测试 ---
订单创建: ORDER_20231119002
错误:订单未支付,无法发货
订单支付成功
订单状态变更: 新建 -> 已支付
订单已取消,退款将在3-5个工作日内到账
订单状态变更: 已支付 -> 已取消
3.2 状态转换图(ASCII)
[新建订单]
|
| pay()
↓
cancel() ←---- [已支付] ----→ ship()
| | ↓
| | [已发货]
| | |
| | | deliver()
| | ↓
+------------→ [已取消] [已送达]
四、生产环境中的实际应用
4.1 文档审批工作流
在企业应用中,文档审批是一个典型的状态模式应用场景:
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 审批记录
*/
class ApprovalRecord {
private String approver;
private String action;
private String comment;
private Date timestamp;
public ApprovalRecord(String approver, String action, String comment) {
this.approver = approver;
this.action = action;
this.comment = comment;
this.timestamp = new Date();
}
@Override
public String toString() {
return String.format("[%s] %s %s: %s", timestamp, approver, action, comment);
}
}
/**
* 文档审批状态接口
*/
interface DocumentState {
void submit(DocumentContext context, String submitter);
void approve(DocumentContext context, String approver, String comment);
void reject(DocumentContext context, String approver, String comment);
void revoke(DocumentContext context);
void publish(DocumentContext context);
String getStateName();
}
/**
* 文档上下文
*/
class DocumentContext {
private String documentId;
private String title;
private String content;
private DocumentState currentState;
private List<ApprovalRecord> approvalHistory;
private int currentApprovalLevel;
private static final int MAX_APPROVAL_LEVEL = 3; // 需要3级审批
public DocumentContext(String documentId, String title, String content) {
this.documentId = documentId;
this.title = title;
this.content = content;
this.currentState = new DraftState();
this.approvalHistory = new ArrayList<>();
this.currentApprovalLevel = 0;
System.out.println("文档创建: " + title);
}
public void setState(DocumentState state) {
this.currentState = state;
}
public void addApprovalRecord(ApprovalRecord record) {
approvalHistory.add(record);
}
public void incrementApprovalLevel() {
currentApprovalLevel++;
}
public void resetApprovalLevel() {
currentApprovalLevel = 0;
}
public boolean isFullyApproved() {
return currentApprovalLevel >= MAX_APPROVAL_LEVEL;
}
public int getCurrentApprovalLevel() {
return currentApprovalLevel;
}
public int getMaxApprovalLevel() {
return MAX_APPROVAL_LEVEL;
}
// 委托给状态对象
public void submit(String submitter) {
currentState.submit(this, submitter);
}
public void approve(String approver, String comment) {
currentState.approve(this, approver, comment);
}
public void reject(String approver, String comment) {
currentState.reject(this, approver, comment);
}
public void revoke() {
currentState.revoke(this);
}
public void publish() {
currentState.publish(this);
}
public String getStateName() {
return currentState.getStateName();
}
public void printApprovalHistory() {
System.out.println("\n审批历史:");
for (ApprovalRecord record : approvalHistory) {
System.out.println(record);
}
}
}
/**
* 草稿状态
*/
class DraftState implements DocumentState {
@Override
public void submit(DocumentContext context, String submitter) {
System.out.println(submitter + " 提交文档进行审批");
context.addApprovalRecord(new ApprovalRecord(submitter, "提交审批", "文档已提交"));
context.setState(new PendingApprovalState());
}
@Override
public void approve(DocumentContext context, String approver, String comment) {
System.out.println("错误:草稿状态无法审批");
}
@Override
public void reject(DocumentContext context, String approver, String comment) {
System.out.println("错误:草稿状态无法驳回");
}
@Override
public void revoke(DocumentContext context) {
System.out.println("错误:草稿状态无需撤回");
}
@Override
public void publish(DocumentContext context) {
System.out.println("错误:草稿状态无法发布");
}
@Override
public String getStateName() {
return "草稿";
}
}
/**
* 待审批状态
*/
class PendingApprovalState implements DocumentState {
@Override
public void submit(DocumentContext context, String submitter) {
System.out.println("错误:文档已在审批中");
}
@Override
public void approve(DocumentContext context, String approver, String comment) {
context.incrementApprovalLevel();
System.out.println(approver + " 审批通过 (第" + context.getCurrentApprovalLevel() +
"级/共" + context.getMaxApprovalLevel() + "级)");
context.addApprovalRecord(new ApprovalRecord(approver, "审批通过", comment));
if (context.isFullyApproved()) {
System.out.println("所有审批已完成,文档进入待发布状态");
context.setState(new ApprovedState());
}
}
@Override
public void reject(DocumentContext context, String approver, String comment) {
System.out.println(approver + " 驳回审批");
context.addApprovalRecord(new ApprovalRecord(approver, "驳回", comment));
context.resetApprovalLevel();
context.setState(new RejectedState());
}
@Override
public void revoke(DocumentContext context) {
System.out.println("撤回审批,文档返回草稿状态");
context.addApprovalRecord(new ApprovalRecord("系统", "撤回", "作者撤回审批"));
context.resetApprovalLevel();
context.setState(new DraftState());
}
@Override
public void publish(DocumentContext context) {
System.out.println("错误:文档审批未完成,无法发布");
}
@Override
public String getStateName() {
return "待审批";
}
}
/**
* 审批通过状态
*/
class ApprovedState implements DocumentState {
@Override
public void submit(DocumentContext context, String submitter) {
System.out.println("错误:文档已审批通过");
}
@Override
public void approve(DocumentContext context, String approver, String comment) {
System.out.println("错误:文档已审批通过");
}
@Override
public void reject(DocumentContext context, String approver, String comment) {
System.out.println("错误:文档已审批通过");
}
@Override
public void revoke(DocumentContext context) {
System.out.println("撤回审批,文档返回草稿状态");
context.addApprovalRecord(new ApprovalRecord("系统", "撤回", "作者撤回审批"));
context.resetApprovalLevel();
context.setState(new DraftState());
}
@Override
public void publish(DocumentContext context) {
System.out.println("文档已发布");
context.addApprovalRecord(new ApprovalRecord("系统", "发布", "文档已正式发布"));
context.setState(new PublishedState());
}
@Override
public String getStateName() {
return "审批通过";
}
}
/**
* 已驳回状态
*/
class RejectedState implements DocumentState {
@Override
public void submit(DocumentContext context, String submitter) {
System.out.println(submitter + " 重新提交文档进行审批");
context.addApprovalRecord(new ApprovalRecord(submitter, "重新提交", "文档已修改并重新提交"));
context.setState(new PendingApprovalState());
}
@Override
public void approve(DocumentContext context, String approver, String comment) {
System.out.println("错误:已驳回的文档无法直接审批");
}
@Override
public void reject(DocumentContext context, String approver, String comment) {
System.out.println("错误:文档已处于驳回状态");
}
@Override
public void revoke(DocumentContext context) {
System.out.println("文档返回草稿状态");
context.setState(new DraftState());
}
@Override
public void publish(DocumentContext context) {
System.out.println("错误:已驳回的文档无法发布");
}
@Override
public String getStateName() {
return "已驳回";
}
}
/**
* 已发布状态
*/
class PublishedState implements DocumentState {
@Override
public void submit(DocumentContext context, String submitter) {
System.out.println("错误:文档已发布,无法提交");
}
@Override
public void approve(DocumentContext context, String approver, String comment) {
System.out.println("错误:文档已发布");
}
@Override
public void reject(DocumentContext context, String approver, String comment) {
System.out.println("错误:文档已发布");
}
@Override
public void revoke(DocumentContext context) {
System.out.println("错误:已发布的文档无法撤回");
}
@Override
public void publish(DocumentContext context) {
System.out.println("文档已发布");
}
@Override
public String getStateName() {
return "已发布";
}
}
/**
* 工作流测试
*/
public class WorkflowStateDemo {
public static void main(String[] args) {
DocumentContext doc = new DocumentContext("DOC001", "年度报告", "这是年度报告内容...");
System.out.println("当前状态: " + doc.getStateName() + "\n");
// 提交审批
doc.submit("张三");
System.out.println("当前状态: " + doc.getStateName() + "\n");
// 第一级审批
doc.approve("经理李四", "内容详实,同意");
System.out.println("当前状态: " + doc.getStateName() + "\n");
// 第二级审批
doc.approve("总监王五", "格式规范,同意");
System.out.println("当前状态: " + doc.getStateName() + "\n");
// 第三级审批
doc.approve("总经理赵六", "最终批准");
System.out.println("当前状态: " + doc.getStateName() + "\n");
// 发布文档
doc.publish();
System.out.println("当前状态: " + doc.getStateName());
// 打印审批历史
doc.printApprovalHistory();
System.out.println("\n\n--- 驳回流程测试 ---");
DocumentContext doc2 = new DocumentContext("DOC002", "项目方案", "项目方案内容...");
doc2.submit("李明");
doc2.approve("经理", "第一级通过");
doc2.reject("总监", "方案需要完善细节");
System.out.println("当前状态: " + doc2.getStateName());
doc2.printApprovalHistory();
}
}
4.2 TCP连接状态管理
TCP协议的连接管理是状态模式的经典应用:
/**
* TCP连接状态接口
*/
interface TCPState {
void open(TCPConnection connection);
void close(TCPConnection connection);
void acknowledge(TCPConnection connection);
String getStateName();
}
/**
* TCP连接上下文
*/
class TCPConnection {
private TCPState state;
private String connectionId;
public TCPConnection(String connectionId) {
this.connectionId = connectionId;
this.state = new TCPClosedState();
System.out.println("TCP连接创建: " + connectionId);
}
public void setState(TCPState state) {
System.out.println("状态转换: " + this.state.getStateName() +
" -> " + state.getStateName());
this.state = state;
}
public void open() {
state.open(this);
}
public void close() {
state.close(this);
}
public void acknowledge() {
state.acknowledge(this);
}
public String getConnectionId() {
return connectionId;
}
public String getStateName() {
return state.getStateName();
}
}
/**
* 关闭状态
*/
class TCPClosedState implements TCPState {
@Override
public void open(TCPConnection connection) {
System.out.println("发送SYN,请求建立连接");
connection.setState(new TCPListenState());
}
@Override
public void close(TCPConnection connection) {
System.out.println("连接已关闭");
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println("错误:连接未打开");
}
@Override
public String getStateName() {
return "CLOSED";
}
}
/**
* 监听状态
*/
class TCPListenState implements TCPState {
@Override
public void open(TCPConnection connection) {
System.out.println("收到SYN-ACK,发送ACK");
connection.setState(new TCPEstablishedState());
}
@Override
public void close(TCPConnection connection) {
System.out.println("关闭连接");
connection.setState(new TCPClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println("等待连接确认");
}
@Override
public String getStateName() {
return "LISTEN";
}
}
/**
* 已建立状态
*/
class TCPEstablishedState implements TCPState {
@Override
public void open(TCPConnection connection) {
System.out.println("连接已建立");
}
@Override
public void close(TCPConnection connection) {
System.out.println("发送FIN,开始关闭连接");
connection.setState(new TCPClosedState());
}
@Override
public void acknowledge(TCPConnection connection) {
System.out.println("发送ACK确认");
}
@Override
public String getStateName() {
return "ESTABLISHED";
}
}
/**
* TCP状态示例
*/
public class TCPStateDemo {
public static void main(String[] args) {
TCPConnection connection = new TCPConnection("192.168.1.100:8080");
System.out.println("初始状态: " + connection.getStateName() + "\n");
// 三次握手
connection.open(); // 客户端发送SYN
connection.open(); // 收到SYN-ACK,发送ACK
System.out.println("当前状态: " + connection.getStateName() + "\n");
// 数据传输
connection.acknowledge();
System.out.println();
// 关闭连接
connection.close();
System.out.println("最终状态: " + connection.getStateName());
}
}
五、开源框架中的状态模式应用
5.1 Spring State Machine
Spring State Machine是Spring官方提供的状态机框架,是状态模式在企业级应用中的典型实现。
/**
* 简化的Spring State Machine使用示例
* 实际使用需要添加Spring State Machine依赖
*/
// 定义状态枚举
enum OrderStatus {
UNPAID, // 未支付
PAID, // 已支付
SHIPPED, // 已发货
DELIVERED, // 已送达
CANCELLED // 已取消
}
// 定义事件枚举
enum OrderEvent {
PAY, // 支付
SHIP, // 发货
DELIVER, // 送达
CANCEL // 取消
}
/**
* 模拟Spring State Machine的配置
*/
class OrderStateMachineConfig {
/**
* 配置状态转换
*/
public void configureTransitions() {
// UNPAID -> PAY -> PAID
// PAID -> SHIP -> SHIPPED
// SHIPPED -> DELIVER -> DELIVERED
// UNPAID/PAID -> CANCEL -> CANCELLED
System.out.println("状态机配置:");
System.out.println("UNPAID --[PAY]--> PAID");
System.out.println("PAID --[SHIP]--> SHIPPED");
System.out.println("SHIPPED --[DELIVER]--> DELIVERED");
System.out.println("UNPAID/PAID --[CANCEL]--> CANCELLED");
}
/**
* 配置状态监听器
*/
public void configureStateListener() {
// 状态进入时的处理
System.out.println("\n配置状态监听器:");
System.out.println("- onEntry: 进入状态时触发");
System.out.println("- onExit: 离开状态时触发");
}
/**
* 配置转换守卫(条件判断)
*/
public void configureGuards() {
System.out.println("\n配置转换守卫:");
System.out.println("- 支付前检查库存");
System.out.println("- 发货前检查地址");
}
/**
* 配置动作
*/
public void configureActions() {
System.out.println("\n配置状态动作:");
System.out.println("- 支付成功后发送通知");
System.out.println("- 发货时生成物流单");
System.out.println("- 送达后发送评价邀请");
}
}
/**
* Spring State Machine使用示例
*/
public class SpringStateMachineDemo {
public static void main(String[] args) {
System.out.println("===== Spring State Machine 概念演示 =====\n");
OrderStateMachineConfig config = new OrderStateMachineConfig();
config.configureTransitions();
config.configureStateListener();
config.configureGuards();
config.configureActions();
System.out.println("\n\n===== 状态转换示例 =====");
simulateOrderFlow();
}
private static void simulateOrderFlow() {
System.out.println("\n订单生命周期:");
System.out.println("1. 创建订单 -> UNPAID");
System.out.println("2. 触发PAY事件 -> PAID");
System.out.println("3. 触发SHIP事件 -> SHIPPED");
System.out.println("4. 触发DELIVER事件 -> DELIVERED");
System.out.println("\nSpring State Machine的优势:");
System.out.println("✓ 声明式配置状态转换");
System.out.println("✓ 支持状态持久化");
System.out.println("✓ 提供状态监听和拦截");
System.out.println("✓ 支持嵌套状态机");
System.out.println("✓ 集成Spring生态");
}
}
5.2 实际Spring State Machine配置示例
/**
* 实际的Spring State Machine配置类结构
* (需要添加相关依赖和注解)
*/
/*
@Configuration
@EnableStateMachine
public class ActualStateMachineConfig
extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states)
throws Exception {
states
.withStates()
.initial(OrderStatus.UNPAID)
.states(EnumSet.allOf(OrderStatus.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.UNPAID).target(OrderStatus.PAID)
.event(OrderEvent.PAY)
.guard(paymentGuard())
.action(paymentAction())
.and()
.withExternal()
.source(OrderStatus.PAID).target(OrderStatus.SHIPPED)
.event(OrderEvent.SHIP)
.action(shipAction())
.and()
.withExternal()
.source(OrderStatus.SHIPPED).target(OrderStatus.DELIVERED)
.event(OrderEvent.DELIVER)
.action(deliverAction());
}
@Bean
public Guard<OrderStatus, OrderEvent> paymentGuard() {
return context -> {
// 支付前的条件检查
return checkInventory() && checkUserBalance();
};
}
@Bean
public Action<OrderStatus, OrderEvent> paymentAction() {
return context -> {
// 支付成功后的动作
sendPaymentNotification();
updateInventory();
};
}
}
*/
5.3 Java线程状态
Java线程的状态转换也是状态模式的应用:
/**
* Java线程状态演示
*/
public class ThreadStateDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("Java线程状态转换图:\n");
printThreadStateTransition();
System.out.println("\n\n实际线程状态演示:");
demonstrateThreadStates();
}
private static void printThreadStateTransition() {
System.out.println(" [NEW]");
System.out.println(" |");
System.out.println(" | start()");
System.out.println(" ↓");
System.out.println(" [RUNNABLE] ←------------------+");
System.out.println(" | |");
System.out.println(" | wait()/join() | notify()/");
System.out.println(" ↓ | notifyAll()");
System.out.println(" [WAITING] -------------------+");
System.out.println(" |");
System.out.println(" | sleep()/wait(timeout)");
System.out.println(" ↓");
System.out.println(" [TIMED_WAITING]");
System.out.println(" |");
System.out.println(" | 等待锁");
System.out.println(" ↓");
System.out.println(" [BLOCKED]");
System.out.println(" |");
System.out.println(" | 执行完毕");
System.out.println(" ↓");
System.out.println(" [TERMINATED]");
}
private static void demonstrateThreadStates() throws InterruptedException {
Thread thread = new Thread(() -> {
try {
System.out.println("线程开始执行");
Thread.sleep(2000); // TIMED_WAITING
System.out.println("线程执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// NEW状态
System.out.println("创建线程后: " + thread.getState());
// 启动线程
thread.start();
Thread.sleep(100);
System.out.println("启动线程后: " + thread.getState());
// TIMED_WAITING状态
Thread.sleep(500);
System.out.println("sleep期间: " + thread.getState());
// 等待线程结束
thread.join();
System.out.println("线程结束后: " + thread.getState());
}
}
六、状态模式的最佳实践
6.1 设计原则
- 单一职责:每个状态类只负责一个状态的行为
- 开闭原则:添加新状态不需要修改已有代码
- 状态封装:状态转换逻辑封装在状态类内部
- 上下文简洁:Context类只负责维护当前状态
6.2 何时使用状态模式
适用场景:
- 对象行为依赖于状态,且状态会在运行时改变
- 代码中包含大量条件判断,且这些判断依赖于对象状态
- 状态转换规则复杂,需要集中管理
- 需要避免重复的状态判断代码
不适用场景:
- 状态很少(只有2-3个状态)
- 状态转换规则简单
- 状态类会导致类数量激增但收益不大
6.3 实现技巧
6.3.1 状态单例模式
/**
* 使用单例模式优化状态对象
* 避免重复创建状态对象
*/
class SingletonOrderState {
private static final NewOrderState NEW_STATE = new NewOrderState();
private static final PaidOrderState PAID_STATE = new PaidOrderState();
private static final ShippedOrderState SHIPPED_STATE = new ShippedOrderState();
public static NewOrderState getNewState() {
return NEW_STATE;
}
public static PaidOrderState getPaidState() {
return PAID_STATE;
}
public static ShippedOrderState getShippedState() {
return SHIPPED_STATE;
}
}
/**
* 使用单例状态
*/
class OptimizedOrderContext {
private OrderState state;
public OptimizedOrderContext() {
this.state = SingletonOrderState.getNewState();
}
public void pay() {
state.pay(this);
// 使用单例状态
setState(SingletonOrderState.getPaidState());
}
public void setState(OrderState state) {
this.state = state;
}
}
6.3.2 状态转换表
import java.util.HashMap;
import java.util.Map;
/**
* 使用状态转换表管理复杂的状态转换
*/
class StateTransitionTable {
private Map<String, Map<String, OrderState>> transitions;
public StateTransitionTable() {
transitions = new HashMap<>();
initializeTransitions();
}
private void initializeTransitions() {
// NEW状态的转换
Map<String, OrderState> fromNew = new HashMap<>();
fromNew.put("PAY", SingletonOrderState.getPaidState());
transitions.put("NEW", fromNew);
// PAID状态的转换
Map<String, OrderState> fromPaid = new HashMap<>();
fromPaid.put("SHIP", SingletonOrderState.getShippedState());
transitions.put("PAID", fromPaid);
// 更多状态转换...
}
public OrderState getNextState(String currentState, String event) {
Map<String, OrderState> stateTransitions = transitions.get(currentState);
if (stateTransitions != null) {
return stateTransitions.get(event);
}
return null;
}
public boolean canTransition(String currentState, String event) {
return getNextState(currentState, event) != null;
}
}
6.3.3 状态持久化
/**
* 支持状态持久化的订单上下文
*/
class PersistentOrderContext {
private String orderId;
private OrderState state;
private StateRepository repository;
public PersistentOrderContext(String orderId, StateRepository repository) {
this.orderId = orderId;
this.repository = repository;
// 从数据库加载状态
loadState();
}
private void loadState() {
String stateName = repository.loadState(orderId);
this.state = createStateFromName(stateName);
}
public void setState(OrderState newState) {
this.state = newState;
// 保存状态到数据库
repository.saveState(orderId, newState.getStateName());
}
private OrderState createStateFromName(String stateName) {
// 根据状态名称创建状态对象
switch (stateName) {
case "新建": return SingletonOrderState.getNewState();
case "已支付": return SingletonOrderState.getPaidState();
// 更多状态...
default: return SingletonOrderState.getNewState();
}
}
}
/**
* 状态仓储接口
*/
interface StateRepository {
String loadState(String orderId);
void saveState(String orderId, String stateName);
}
七、状态模式与策略模式的区别
状态模式和策略模式在结构上很相似,但用途不同:
状态模式 (State Pattern):
目的:根据状态改变行为
特点:
- 状态之间可以相互转换
- 状态转换通常由状态对象自己决定
- 客户端通常不知道具体状态类
- 重点在"是什么状态"
示例:
订单的状态:新建 -> 已支付 -> 已发货
状态之间有转换关系
Context
|
| 持有并会改变
↓
State A ──→ State B ──→ State C
策略模式 (Strategy Pattern):
目的:封装算法,使它们可以互换
特点:
- 策略之间是平等的,不互相转换
- 策略选择通常由客户端决定
- 客户端需要知道所有策略
- 重点在"怎么做"
示例:
支付方式:支付宝、微信、银行卡
策略之间相互独立
Context
|
| 持有但不改变
↓
Strategy接口
↗ ↑ ↖
Strategy A B C
八、状态模式的注意事项
8.1 常见陷阱
- 状态类爆炸:状态过多导致类数量激增
// 解决方案:合并相似状态或使用状态组合
class CompositeState implements OrderState {
private List<OrderState> states = new ArrayList<>();
public void addState(OrderState state) {
states.add(state);
}
}
- 状态转换混乱:缺乏清晰的状态转换规则
// 解决方案:使用状态转换表或状态机框架
// 绘制状态转换图,明确所有可能的转换
- 状态共享问题:使用单例状态时的并发问题
// 解决方案:确保状态类是无状态的(stateless)
// 所有数据都存储在Context中
class StatelessOrderState implements OrderState {
// 不包含任何实例变量
// 所有操作都基于传入的context
@Override
public void pay(OrderContext context) {
// 使用context中的数据,不使用自身状态
}
}
8.2 性能优化
- 使用享元模式:共享状态对象
- 延迟加载:按需创建状态对象
- 状态缓存:缓存频繁使用的状态
/**
* 状态工厂 + 缓存
*/
class StateFactory {
private static Map<String, OrderState> stateCache = new ConcurrentHashMap<>();
public static OrderState getState(String stateName) {
return stateCache.computeIfAbsent(stateName, key -> {
switch (key) {
case "NEW": return new NewOrderState();
case "PAID": return new PaidOrderState();
default: throw new IllegalArgumentException("Unknown state: " + key);
}
});
}
}
九、总结
状态模式是处理对象状态变化的强大工具,它通过将状态相关的行为封装到独立的状态类中,消除了复杂的条件判断,提高了代码的可维护性和扩展性。
9.1 核心要点
- 消除条件判断:用多态替代if-else
- 封装状态行为:每个状态类负责自己的行为
- 清晰的状态转换:状态转换规则明确
- 易于扩展:添加新状态不影响现有代码
9.2 优缺点总结
优点:
- 结构清晰,将状态相关行为局部化
- 消除了庞大的条件分支语句
- 易于增加新的状态
- 状态转换显式化
- 符合单一职责原则和开闭原则
缺点:
- 会增加系统类的数量
- 状态模式的结构与实现较为复杂
- 如果状态较少,使用状态模式会显得繁琐
- 状态转换逻辑分散可能导致难以理解整体流程
9.3 使用建议
- 适度使用:状态少于5个时考虑简单的if-else
- 状态可视化:绘制状态转换图,便于理解和维护
- 使用框架:复杂场景考虑使用Spring State Machine等框架
- 状态持久化:关键业务状态需要持久化到数据库
- 日志记录:记录所有状态转换,便于问题排查
9.4 最佳实践
- 小型项目:状态简单时使用枚举 + switch即可
- 中型项目:状态复杂时使用状态模式
- 大型项目:使用状态机框架(Spring State Machine)
- 分布式系统:考虑使用工作流引擎(Activiti、Camunda)
状态模式不仅仅是一种设计模式,更是一种面向对象的思维方式。通过合理运用状态模式,我们可以让代码更加优雅、更易维护。希望本文能帮助你在实际项目中更好地应用状态模式。