状态模式
引言
在软件开发中,当对象需要根据内部状态变化改变行为时(如订单状态流转、游戏角色动作切换),传统的if-else或switch-case会导致代码臃肿和维护困难。状态模式通过封装状态行为与解耦状态转换,实现了对象行为的动态切换,成为复杂状态管理的优雅解决方案。
诞生背景
GoF在《设计模式》中提出状态模式,解决三大核心痛点:
- 行为耦合:对象行为与状态强绑定,修改状态需改动所有相关方法
- 条件爆炸:状态判断逻辑随状态数量指数级增长
- 扩展困难:新增状态需修改所有状态转换代码
演进过程
- GoF基础(1994):确立状态接口与上下文协作关系
- 工作流引擎演进:BPMN等系统将状态模式扩展为可视化状态机
- 现代应用:前端框架的状态管理库(如Redux、XState)实现状态模式的工程化
核心概念
- 上下文(Context):持有当前状态对象,定义客户端接口
- 抽象状态(State):声明状态特定行为的接口
- 具体状态(ConcreteState):实现当前状态对应的行为逻辑
通用实现
Java 实现
java
// 抽象状态
interface OrderState {
void next(OrderContext context);
void prev(OrderContext context);
}
// 具体状态:已下单
class PlacedState implements OrderState {
public void next(OrderContext context) {
context.setState(new ShippedState());
}
public void prev(OrderContext context) {
System.out.println("已是最初状态");
}
}
// 具体状态:已发货
class ShippedState implements OrderState {
public void next(OrderContext context) {
context.setState(new DeliveredState());
}
public void prev(OrderContext context) {
context.setState(new PlacedState());
}
}
// 上下文
class OrderContext {
private OrderState state;
public OrderContext() {
this.state = new PlacedState(); // 初始状态
}
public void setState(OrderState state) {
this.state = state;
}
public void nextState() {
state.next(this);
}
public void prevState() {
state.prev(this);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
OrderContext order = new OrderContext();
order.nextState(); // 状态切换:已下单 → 已发货
}
}PHP 实现
php
// 抽象状态
interface ElevatorState {
public function openDoors(): void;
public function closeDoors(): void;
}
// 具体状态:停止状态
class StoppedState implements ElevatorState {
public function openDoors(): void {
echo "开门中...\n";
}
public function closeDoors(): void {
echo "门已关闭\n";
}
}
// 具体状态:运行状态
class RunningState implements ElevatorState {
public function openDoors(): void {
throw new Exception("运行中不能开门!");
}
public function closeDoors(): void {
echo "门已关闭\n";
}
}
// 上下文
class ElevatorContext {
private ElevatorState $state;
public function __construct() {
$this->state = new StoppedState(); // 初始状态
}
public function setState(ElevatorState $state): void {
$this->state = $state;
}
public function requestOpen(): void {
$this->state->openDoors();
}
public function startMoving(): void {
$this->state = new RunningState();
echo "电梯开始运行\n";
}
}
// 客户端
$elevator = new ElevatorContext();
$elevator->requestOpen(); // 开门成功
$elevator->startMoving();
$elevator->requestOpen(); // 抛出异常:运行中不能开门应用场景
- 工作流引擎:订单状态流转(待付款→已发货→已完成)
- 游戏开发:角色状态切换(站立→奔跑→跳跃)
- 硬件控制:设备状态管理(待机→运行→故障)
- UI交互:组件状态变更(禁用→激活→加载中)
案例:音乐播放器状态控制
Java 实现
java
// 抽象状态
interface PlayerState {
void play(PlayerContext context);
void pause(PlayerContext context);
}
// 具体状态:播放状态
class PlayingState implements PlayerState {
public void play(PlayerContext context) {
System.out.println("已在播放中");
}
public void pause(PlayerContext context) {
context.setState(new PausedState());
System.out.println("暂停播放");
}
}
// 具体状态:暂停状态
class PausedState implements PlayerState {
public void play(PlayerContext context) {
context.setState(new PlayingState());
System.out.println("继续播放");
}
public void pause(PlayerContext context) {
System.out.println("已是暂停状态");
}
}
// 上下文
class PlayerContext {
private PlayerState state = new PausedState();
public void setState(PlayerState state) {
this.state = state;
}
public void pressPlay() {
state.play(this);
}
public void pressPause() {
state.pause(this);
}
}
// 客户端
PlayerContext player = new PlayerContext();
player.pressPlay(); // 继续播放
player.pressPause(); // 暂停播放PHP 实现
php
// 抽象状态
interface TrafficLightState {
public function change(TrafficLightContext $context): void;
}
// 具体状态:红灯
class RedLight implements TrafficLightState {
public function change(TrafficLightContext $context): void {
echo "红灯 → 绿灯\n";
$context->setState(new GreenLight());
}
}
// 具体状态:绿灯
class GreenLight implements TrafficLightState {
public function change(TrafficLightContext $context): void {
echo "绿灯 → 黄灯\n";
$context->setState(new YellowLight());
}
}
// 具体状态:黄灯
class YellowLight implements TrafficLightState {
public function change(TrafficLightContext $context): void {
echo "黄灯 → 红灯\n";
$context->setState(new RedLight());
}
}
// 上下文
class TrafficLightContext {
private TrafficLightState $state;
public function __construct() {
$this->state = new RedLight();
}
public function setState(TrafficLightState $state): void {
$this->state = $state;
}
public function changeLight(): void {
$this->state->change($this);
}
}
// 客户端
$trafficLight = new TrafficLightContext();
$trafficLight->changeLight(); // 红灯 → 绿灯
$trafficLight->changeLight(); // 绿灯 → 黄灯
$trafficLight->changeLight(); // 黄灯 → 红灯优点
- 开闭原则:新增状态无需修改现有代码
- 消除条件语句:用多态替代复杂的条件判断
- 职责清晰:每个状态类封装特定行为逻辑
- 状态转换显式化:转换逻辑集中在状态类中
缺点
- 类数量膨胀:状态过多导致类数量增加
- 上下文依赖:状态类需了解上下文信息
- 转换逻辑分散:状态转换逻辑分布在多个状态类中
扩展
- 状态共享:无内部状态的状态对象可设计为享元(Flyweight)
- 状态表驱动:使用Map存储状态转换规则(状态→事件→新状态)
- 层次化状态:通过继承实现状态行为的复用(如基类处理通用逻辑)
模式协作
- 与策略模式:状态模式根据状态改变行为,策略模式主动选择算法
- 与观察者模式:状态变更时可通知观察者(如订单状态更新通知)
- 与单例模式:无状态的状态对象可设计为单例
延伸思考
- 分布式状态机:Saga模式实现跨服务的分布式状态管理
- 前端状态管理:XState库将状态模式引入前端工作流
- AI行为树:游戏AI使用分层状态机实现复杂行为逻辑
总结
状态模式是行为动态切换的架构师,通过封装状态行为与解耦状态转换,实现了对象行为的优雅变化。其核心价值在于:消除条件分支的简洁之道与开闭原则的完美实践。在订单系统、游戏引擎、硬件控制等场景中,状态模式能显著提升代码可维护性和扩展性,成为状态驱动型系统的核心架构模式。