【从零入门23种设计模式20】行为型之状态模式
·
一、状态模式核心定义
状态模式是行为型设计模式的一种,核心目的是:
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
简单来说:将对象的每一种状态封装成独立的类,对象的行为由当前状态决定;状态切换时,对象会委托给对应状态类处理,而非通过大量 if/else 或 switch 判断。
核心解决的问题
- 消除状态判断分支:避免在一个类中用大量 if/else/switch 判断不同状态的行为(如订单类中判断 “已创建 / 已支付 / 已发货 / 已取消” 的逻辑);
- 状态行为封装:每种状态的行为封装到独立类中,符合单一职责原则;
- 状态切换可控:状态的转换规则集中管理,可灵活扩展新状态;
- 行为与状态解耦:对象无需知道状态的具体实现,只需委托状态类处理行为。
生活类比
- 场景 1:电梯运行状态
- 环境类(Context):电梯本身;
- 状态类:停止状态、运行状态、开门状态、关门状态;
- 核心:电梯的行为(开门、关门、上行、下行)由当前状态决定 —— 开门状态下无法上行,停止状态下才能开门,运行状态下只能下行 / 上行。
- 场景 2:订单状态流转
- 环境类:订单对象;
- 状态类:已创建、已支付、已发货、已收货、已取消;
- 核心:订单的行为(支付、发货、取消)由当前状态决定 —— 已创建状态可支付,已支付状态可发货 / 取消,已发货状态不可取消。
- 场景 3:游戏角色状态
- 环境类:游戏角色;
- 状态类:正常状态、攻击状态、防御状态、死亡状态;
- 核心:角色的行为(攻击、防御、移动)由当前状态决定 —— 死亡状态下无法攻击 / 移动,防御状态下伤害减免。
标准角色
| 角色 | 职责 | 类比(订单场景) | 代码定位 |
|---|---|---|---|
| 环境类(Context) | 维护当前状态,委托当前状态处理行为;提供状态切换的入口 | 订单对象(持有当前状态,调用状态行为) | 业务核心对象(如Order、Elevator) |
| 抽象状态(State) | 定义所有状态的通用行为接口(如订单的支付、取消、发货方法) | 订单状态接口(pay ()、cancel ()、ship ()) | 接口 / 抽象类 |
| 具体状态(ConcreteState) | 实现抽象状态接口,封装对应状态的行为;定义状态转换规则(如已创建状态支付后切换为已支付状态) | 已创建状态、已支付状态、已取消状态 | 各状态的实现类 |
核心 UML 类图

二、订单状态流转
以 “订单状态流转” 为例,实现状态模式的核心逻辑 —— 覆盖所有核心角色,消除 if/else 判断,通过状态类封装不同状态的行为。
1. 步骤 1:定义抽象状态(订单状态接口)
/**
* 抽象状态:订单状态接口(定义所有状态的通用行为)
*/
public interface OrderState {
/**
* 支付订单
*/
void pay(OrderContext context);
/**
* 取消订单
*/
void cancel(OrderContext context);
/**
* 发货订单
*/
void ship(OrderContext context);
/**
* 获取当前状态名称
*/
String getStateName();
}
2. 步骤 2:实现具体状态(各订单状态)
/**
* 具体状态1:已创建状态(初始状态)
*/
public class CreatedState implements OrderState {
@Override
public void pay(OrderContext context) {
// 已创建状态可支付,支付后切换为已支付状态
System.out.println("【已创建状态】订单支付成功,状态切换为已支付");
context.setState(new PaidState());
}
@Override
public void cancel(OrderContext context) {
// 已创建状态可取消,取消后切换为已取消状态
System.out.println("【已创建状态】订单取消成功,状态切换为已取消");
context.setState(new CancelledState());
}
@Override
public void ship(OrderContext context) {
// 已创建状态不可发货
System.out.println("【已创建状态】订单未支付,无法发货");
}
@Override
public String getStateName() {
return "已创建";
}
}
/**
* 具体状态2:已支付状态
*/
public class PaidState implements OrderState {
@Override
public void pay(OrderContext context) {
// 已支付状态不可重复支付
System.out.println("【已支付状态】订单已支付,无需重复支付");
}
@Override
public void cancel(OrderContext context) {
// 已支付状态可取消,取消后切换为已取消状态
System.out.println("【已支付状态】订单取消成功,状态切换为已取消");
context.setState(new CancelledState());
}
@Override
public void ship(OrderContext context) {
// 已支付状态可发货,发货后切换为已发货状态
System.out.println("【已支付状态】订单发货成功,状态切换为已发货");
context.setState(new ShippedState());
}
@Override
public String getStateName() {
return "已支付";
}
}
/**
* 具体状态3:已发货状态
*/
public class ShippedState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("【已发货状态】订单已发货,无需支付");
}
@Override
public void cancel(OrderContext context) {
System.out.println("【已发货状态】订单已发货,无法取消");
}
@Override
public void ship(OrderContext context) {
System.out.println("【已发货状态】订单已发货,无需重复发货");
}
@Override
public String getStateName() {
return "已发货";
}
}
/**
* 具体状态4:已取消状态
*/
public class CancelledState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("【已取消状态】订单已取消,无法支付");
}
@Override
public void cancel(OrderContext context) {
System.out.println("【已取消状态】订单已取消,无需重复取消");
}
@Override
public void ship(OrderContext context) {
System.out.println("【已取消状态】订单已取消,无法发货");
}
@Override
public String getStateName() {
return "已取消";
}
}
3. 步骤 3:实现环境类(订单上下文)
/**
* 环境类:订单上下文(维护当前状态,委托状态处理行为)
*/
public class OrderContext {
// 订单ID
private String orderId;
// 当前状态(核心:委托该状态处理所有行为)
private OrderState currentState;
/**
* 初始化订单,默认状态为已创建
*/
public OrderContext(String orderId) {
this.orderId = orderId;
this.currentState = new CreatedState(); // 初始状态
System.out.println("【订单上下文】订单创建成功 | 订单ID:" + orderId + ",初始状态:" + currentState.getStateName());
}
/**
* 切换状态(供状态类调用)
*/
public void setState(OrderState state) {
this.currentState = state;
System.out.println("【订单上下文】订单状态更新 | 订单ID:" + orderId + ",当前状态:" + currentState.getStateName());
}
/**
* 支付订单(委托当前状态处理)
*/
public void pay() {
System.out.println("\n【订单上下文】发起支付请求 | 订单ID:" + orderId);
currentState.pay(this);
}
/**
* 取消订单(委托当前状态处理)
*/
public void cancel() {
System.out.println("\n【订单上下文】发起取消请求 | 订单ID:" + orderId);
currentState.cancel(this);
}
/**
* 发货订单(委托当前状态处理)
*/
public void ship() {
System.out.println("\n【订单上下文】发起发货请求 | 订单ID:" + orderId);
currentState.ship(this);
}
// Getter
public String getOrderId() {
return orderId;
}
public String getCurrentStateName() {
return currentState.getStateName();
}
}
4. 客户端(测试订单状态流转)
/**
* 客户端:测试订单状态流转(状态模式)
*/
public class StateClient {
public static void main(String[] args) {
// 1. 创建订单上下文(初始状态:已创建)
OrderContext order = new OrderContext("O20240314001");
// 2. 测试1:已创建状态下支付
order.pay();
// 3. 测试2:已支付状态下发货
order.ship();
// 4. 测试3:已发货状态下取消(不允许)
order.cancel();
// 5. 重新创建订单,测试取消
System.out.println("\n======= 测试新订单取消 =======");
OrderContext order2 = new OrderContext("O20240314002");
order2.cancel();
// 6. 已取消状态下支付(不允许)
order2.pay();
}
}
输出结果
【订单上下文】订单创建成功 | 订单ID:O20240314001,初始状态:已创建
【订单上下文】发起支付请求 | 订单ID:O20240314001
【已创建状态】订单支付成功,状态切换为已支付
【订单上下文】订单状态更新 | 订单ID:O20240314001,当前状态:已支付
【订单上下文】发起发货请求 | 订单ID:O20240314001
【已支付状态】订单发货成功,状态切换为已发货
【订单上下文】订单状态更新 | 订单ID:O20240314001,当前状态:已发货
【订单上下文】发起取消请求 | 订单ID:O20240314001
【已发货状态】订单已发货,无法取消
======= 测试新订单取消 =======
【订单上下文】订单创建成功 | 订单ID:O20240314002,初始状态:已创建
【订单上下文】发起取消请求 | 订单ID:O20240314002
【已创建状态】订单取消成功,状态切换为已取消
【订单上下文】订单状态更新 | 订单ID:O20240314002,当前状态:已取消
【订单上下文】发起支付请求 | 订单ID:O20240314002
【已取消状态】订单已取消,无法支付
三、Spring 实战版(订单状态机 + 注解驱动)
在业务开发中,状态模式最核心的实战场景是结合 Spring 实现可配置的订单状态机—— 通过 Spring 的依赖注入管理状态类,通过注解简化状态切换,支持动态扩展新状态。
1. 依赖准备(Spring Boot)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring状态机(可选,简化状态流转配置) -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
2. 核心模型定义
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 订单状态枚举(统一状态标识)
*/
@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
CREATED("CREATED", "已创建"),
PAID("PAID", "已支付"),
SHIPPED("SHIPPED", "已发货"),
RECEIVED("RECEIVED", "已收货"),
CANCELLED("CANCELLED", "已取消");
private final String code;
private final String desc;
}
/**
* 订单事件枚举(触发状态切换的事件)
*/
@Getter
@AllArgsConstructor
public enum OrderEventEnum {
PAY("PAY", "支付"),
CANCEL("CANCEL", "取消"),
SHIP("SHIP", "发货"),
RECEIVE("RECEIVE", "确认收货");
private final String code;
private final String desc;
}
3. 抽象状态(Spring Bean 接口)
import org.springframework.stereotype.Component;
/**
* 抽象状态:订单状态接口(标记为Spring组件,便于注入)
*/
public interface OrderState {
/**
* 处理事件,返回新状态(核心:状态转换规则)
*/
OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event);
/**
* 获取当前状态枚举
*/
OrderStatusEnum getStatus();
}
4. 具体状态(Spring Bean 实现)
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 具体状态1:已创建状态(Spring Bean)
*/
@Slf4j
@Component
public class CreatedState implements OrderState {
@Override
public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
switch (event) {
case PAY:
log.info("【已创建状态】处理支付事件 | 订单ID:{}", context.getOrderId());
return OrderStatusEnum.PAID; // 支付后切换为已支付
case CANCEL:
log.info("【已创建状态】处理取消事件 | 订单ID:{}", context.getOrderId());
return OrderStatusEnum.CANCELLED; // 取消后切换为已取消
default:
log.warn("【已创建状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
return context.getCurrentStatus(); // 不切换状态
}
}
@Override
public OrderStatusEnum getStatus() {
return OrderStatusEnum.CREATED;
}
}
/**
* 具体状态2:已支付状态(Spring Bean)
*/
@Slf4j
@Component
public class PaidState implements OrderState {
@Override
public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
switch (event) {
case SHIP:
log.info("【已支付状态】处理发货事件 | 订单ID:{}", context.getOrderId());
return OrderStatusEnum.SHIPPED; // 发货后切换为已发货
case CANCEL:
log.info("【已支付状态】处理取消事件 | 订单ID:{}", context.getOrderId());
return OrderStatusEnum.CANCELLED; // 取消后切换为已取消
default:
log.warn("【已支付状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
return context.getCurrentStatus();
}
}
@Override
public OrderStatusEnum getStatus() {
return OrderStatusEnum.PAID;
}
}
/**
* 具体状态3:已发货状态(Spring Bean)
*/
@Slf4j
@Component
public class ShippedState implements OrderState {
@Override
public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
switch (event) {
case RECEIVE:
log.info("【已发货状态】处理确认收货事件 | 订单ID:{}", context.getOrderId());
return OrderStatusEnum.RECEIVED; // 确认收货后切换为已收货
default:
log.warn("【已发货状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
return context.getCurrentStatus();
}
}
@Override
public OrderStatusEnum getStatus() {
return OrderStatusEnum.SHIPPED;
}
}
/**
* 具体状态4:已取消状态(Spring Bean)
*/
@Slf4j
@Component
public class CancelledState implements OrderState {
@Override
public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) {
log.warn("【已取消状态】不支持任何事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc());
return context.getCurrentStatus(); // 已取消状态不切换
}
@Override
public OrderStatusEnum getStatus() {
return OrderStatusEnum.CANCELLED;
}
}
5. 环境类(Spring 管理的订单上下文)
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 环境类:订单上下文(Spring Bean,管理状态映射和当前状态)
*/
@Data
@Component
public class OrderContext {
private String orderId;
private OrderStatusEnum currentStatus;
// 状态映射:状态枚举 → 状态实例(Spring注入所有状态类)
private final Map<OrderStatusEnum, OrderState> stateMap = new ConcurrentHashMap<>();
/**
* 构造函数:注入所有OrderState实现类,初始化状态映射
*/
@Autowired
public OrderContext(Map<String, OrderState> stateBeans) {
// 将Spring容器中的所有OrderState Bean放入映射
for (OrderState state : stateBeans.values()) {
stateMap.put(state.getStatus(), state);
}
log.info("【订单上下文】初始化状态映射,共加载{}个状态类", stateMap.size());
}
/**
* 初始化订单
*/
public void init(String orderId) {
this.orderId = orderId;
this.currentStatus = OrderStatusEnum.CREATED; // 初始状态
log.info("【订单上下文】订单初始化 | 订单ID:{},初始状态:{}", orderId, currentStatus.getDesc());
}
/**
* 触发事件(核心:委托当前状态处理事件,切换状态)
*/
public void triggerEvent(OrderEventEnum event) {
log.info("\n【订单上下文】触发事件 | 订单ID:{},当前状态:{},事件:{}",
orderId, currentStatus.getDesc(), event.getDesc());
// 获取当前状态实例
OrderState currentState = stateMap.get(currentStatus);
if (currentState == null) {
log.error("【订单上下文】未找到状态实例 | 状态:{}", currentStatus.getDesc());
return;
}
// 委托当前状态处理事件,获取新状态
OrderStatusEnum newStatus = currentState.handleEvent(this, event);
if (!newStatus.equals(currentStatus)) {
this.currentStatus = newStatus;
log.info("【订单上下文】状态切换成功 | 订单ID:{},新状态:{}", orderId, newStatus.getDesc());
}
}
}
6. 订单服务(业务入口)
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 订单服务(业务入口,封装状态机调用)
*/
@Slf4j
@Service
public class OrderService {
@Resource
private OrderContext orderContext;
/**
* 创建订单
*/
public void createOrder(String orderId) {
orderContext.init(orderId);
}
/**
* 支付订单
*/
public void payOrder(String orderId) {
orderContext.setOrderId(orderId); // 绑定订单ID
orderContext.triggerEvent(OrderEventEnum.PAY);
}
/**
* 取消订单
*/
public void cancelOrder(String orderId) {
orderContext.setOrderId(orderId);
orderContext.triggerEvent(OrderEventEnum.CANCEL);
}
/**
* 发货订单
*/
public void shipOrder(String orderId) {
orderContext.setOrderId(orderId);
orderContext.triggerEvent(OrderEventEnum.SHIP);
}
/**
* 确认收货
*/
public void receiveOrder(String orderId) {
orderContext.setOrderId(orderId);
orderContext.triggerEvent(OrderEventEnum.RECEIVE);
}
}
7. 客户端(Spring Boot 测试)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 客户端:测试Spring版订单状态机
*/
@SpringBootApplication
public class SpringStateDemoApplication {
public static void main(String[] args) {
// 1. 启动Spring容器(自动加载所有状态类和上下文)
ConfigurableApplicationContext context = SpringApplication.run(SpringStateDemoApplication.class, args);
OrderService orderService = context.getBean(OrderService.class);
// 2. 测试订单状态流转
System.out.println("======= 测试订单状态流转 =======");
String orderId = "O20240314003";
// 创建订单(初始状态:已创建)
orderService.createOrder(orderId);
// 支付订单(已创建→已支付)
orderService.payOrder(orderId);
// 发货订单(已支付→已发货)
orderService.shipOrder(orderId);
// 确认收货(已发货→已收货)
orderService.receiveOrder(orderId);
// 尝试取消已收货订单(不支持)
orderService.cancelOrder(orderId);
context.close();
}
}
输出结果
【订单上下文】初始化状态映射,共加载4个状态类
======= 测试订单状态流转 =======
【订单上下文】订单初始化 | 订单ID:O20240314003,初始状态:已创建
【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已创建,事件:支付
【已创建状态】处理支付事件 | 订单ID:O20240314003
【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已支付
【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已支付,事件:发货
【已支付状态】处理发货事件 | 订单ID:O20240314003
【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已发货
【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已发货,事件:确认收货
【已发货状态】处理确认收货事件 | 订单ID:O20240314003
【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已收货
【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已收货,事件:取消
【已收货状态】不支持事件 | 订单ID:O20240314003,事件:取消
四、状态模式的核心特点与适用场景
优点
- 消除分支判断:替代大量 if/else/switch,代码更简洁、易维护;
- 状态行为封装:每种状态的行为封装到独立类中,符合单一职责原则;
- 状态扩展灵活:新增状态只需实现抽象状态接口,无需修改原有代码(开闭原则);
- 状态切换可控:状态转换规则集中在状态类中,便于管理和修改;
- 行为与状态解耦:环境类无需知道状态的具体实现,只需委托状态类处理。
缺点
- 类数量膨胀:每种状态对应一个类,状态较多时会产生大量状态类(如 10 种状态 = 10 个状态类);
- 状态逻辑分散:状态转换规则分散在各状态类中,复杂状态流转时难以全局把控;
- 依赖状态类:环境类依赖所有状态类,增加了类之间的关联;
- 简单场景冗余:简单的状态判断(如 2-3 种状态)使用状态模式会增加复杂度。
适用场景
- 对象行为随状态变化:如订单状态流转、电梯运行状态、游戏角色状态;
- 存在大量状态判断分支:类中包含大量 if/else/switch 判断不同状态的行为;
- 状态流转规则明确:状态之间的转换规则清晰,且可能需要扩展;
- 工作流 / 审批流程:如请假审批(待审批 / 审批中 / 已通过 / 已驳回)、合同审批;
- 有限状态机(FSM):如通信协议状态、设备运行状态、状态机框架开发。
五、JDK/ Spring 中的原生应用
状态模式在框架中主要用于状态驱动的行为控制,以下是核心场景:
1. Spring StateMachine(Spring 状态机)
- 抽象状态:
State; - 具体状态:自定义
State实现; - 环境类:
StateMachine; - 核心逻辑:基于状态模式实现可配置的状态机,支持状态流转、事件触发、监听器扩展。
2. Java 线程状态(Thread.State)
- 抽象状态:
Thread.State(枚举); - 具体状态:
NEW、RUNNABLE、BLOCKED、WAITING、TERMINATED; - 环境类:
Thread; - 核心逻辑:线程的行为(start ()、wait ()、notify ())由当前状态决定,如 NEW 状态的线程只能 start (),TERMINATED 状态的线程无法再次 start ()。
3. JDBC 连接状态(Connection)
- 抽象状态:连接状态(未连接 / 已连接 / 已关闭);
- 具体状态:
ClosedState、ConnectedState、IdleState; - 环境类:
Connection; - 核心逻辑:连接的行为(execute ()、close ()、commit ())由当前状态决定,如已关闭的连接无法执行 SQL。
4. Servlet 生命周期(Servlet)
- 抽象状态:
Servlet生命周期状态(初始化 / 运行 / 销毁); - 具体状态:
InitState、ServiceState、DestroyState; - 环境类:
HttpServlet; - 核心逻辑:Servlet 的行为(init ()、service ()、destroy ())由当前生命周期状态决定。
5. Netty Channel 状态(ChannelState)
- 抽象状态:
ChannelState; - 具体状态:
CHANNEL_OPEN、CHANNEL_ACTIVE、CHANNEL_INACTIVE、CHANNEL_CLOSED; - 环境类:
Channel; - 核心逻辑:Channel 的 I/O 行为(read ()、write ())由当前状态决定,如 CLOSED 状态的 Channel 无法读写。
六、状态模式 vs 策略模式(易混淆点)
状态模式和策略模式的类结构几乎一致,但设计意图完全不同:
| 维度 | 状态模式 | 策略模式 |
|---|---|---|
| 核心目的 | 处理状态驱动的行为变化,状态由对象内部决定 | 处理算法 / 策略的替换,策略由外部传入 |
| 状态 / 策略切换 | 内部自动切换(如订单支付后自动切换为已支付状态) | 外部手动切换(如选择不同的排序算法) |
| 状态 / 策略关系 | 状态之间有明确的流转规则(如已创建→已支付) | 策略之间相互独立,无依赖关系 |
| 关注点 | 状态与行为的绑定 | 算法的灵活替换 |
| 典型场景 | 订单状态、电梯状态、生命周期管理 | 排序算法、支付方式、校验规则 |
总结
- 状态模式的核心是将对象的每种状态封装为独立类,对象行为由当前状态决定,通过状态类委托处理而非分支判断;
- 核心角色包括环境类(维护当前状态)、抽象状态(定义行为接口)、具体状态(封装状态行为和转换规则);
- 状态模式适用于对象行为随状态变化、存在大量状态判断分支的场景,如订单状态流转、工作流审批;
- 状态模式与策略模式结构相似但意图不同:状态模式关注内部状态驱动的行为变化,策略模式关注外部策略的灵活替换。

更多推荐
所有评论(0)