状态模式(State)
前言
状态模式允许对象在内部状态改变时改变其行为。它将状态逻辑从对象中分离,避免大量条件语句,是状态机、工作流、游戏开发的经典模式。本文将详细讲解状态模式的核心原理及实际应用。
一、核心概念
1.1 定义
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
1.2 核心角色
| 角色 | 说明 |
|---|---|
| 上下文(Context) | 持有当前状态,委托状态处理 |
| 状态接口(State) | 定义状态行为接口 |
| 具体状态(ConcreteState) | 实现特定状态的行为 |
1.3 与策略模式的区别
| 维度 | 策略模式 | 状态模式 |
|---|---|---|
| 用途 | 算法切换 | 状态切换 |
| 切换方式 | 客户端决定 | 状态自动切换 |
| 关系 | 策略间独立 | 状态间有转换关系 |
| 生命周期 | 一次性使用 | 持续转换 |
二、代码实现
2.1 基础实现
php
<?php
// 状态接口
interface State
{
public function handle(Context $context): void;
}
// 上下文
class Context
{
private State $state;
public function __construct(State $state)
{
$this->state = $state;
}
public function setState(State $state): void
{
$this->state = $state;
}
public function request(): void
{
$this->state->handle($this);
}
}
// 具体状态 A
class ConcreteStateA implements State
{
public function handle(Context $context): void
{
echo "State A: handling request" . PHP_EOL;
$context->setState(new ConcreteStateB());
}
}
// 具体状态 B
class ConcreteStateB implements State
{
public function handle(Context $context): void
{
echo "State B: handling request" . PHP_EOL;
$context->setState(new ConcreteStateA());
}
}
// 使用
$context = new Context(new ConcreteStateA());
$context->request(); // State A → B
$context->request(); // State B → A
$context->request(); // State A → B2.2 实际案例:订单状态机
php
<?php
// 订单状态接口
interface OrderState
{
public function pay(Order $order): void;
public function ship(Order $order): void;
public function deliver(Order $order): void;
public function cancel(Order $order): void;
}
// 上下文:订单
class Order
{
private OrderState $state;
public string $id;
public function __construct(string $id)
{
$this->id = $id;
$this->state = new PendingState();
}
public function setState(OrderState $state): void
{
$this->state = $state;
}
public function pay(): void
{
$this->state->pay($this);
}
public function ship(): void
{
$this->state->ship($this);
}
public function deliver(): void
{
$this->state->deliver($this);
}
public function cancel(): void
{
$this->state->cancel($this);
}
}
// 待支付状态
class PendingState implements OrderState
{
public function pay(Order $order): void
{
echo "Order {$order->id}: Payment received" . PHP_EOL;
$order->setState(new PaidState());
}
public function ship(Order $order): void
{
echo "Cannot ship: order not paid" . PHP_EOL;
}
public function deliver(Order $order): void
{
echo "Cannot deliver: order not paid" . PHP_EOL;
}
public function cancel(Order $order): void
{
echo "Order {$order->id}: Cancelled" . PHP_EOL;
$order->setState(new CancelledState());
}
}
// 已支付状态
class PaidState implements OrderState
{
public function pay(Order $order): void
{
echo "Already paid" . PHP_EOL;
}
public function ship(Order $order): void
{
echo "Order {$order->id}: Shipped" . PHP_EOL;
$order->setState(new ShippedState());
}
public function deliver(Order $order): void
{
echo "Cannot deliver: not shipped" . PHP_EOL;
}
public function cancel(Order $order): void
{
echo "Order {$order->id}: Refunded and cancelled" . PHP_EOL;
$order->setState(new CancelledState());
}
}
// 已发货状态
class ShippedState implements OrderState
{
public function pay(Order $order): void
{
echo "Already paid" . PHP_EOL;
}
public function ship(Order $order): void
{
echo "Already shipped" . PHP_EOL;
}
public function deliver(Order $order): void
{
echo "Order {$order->id}: Delivered" . PHP_EOL;
$order->setState(new DeliveredState());
}
public function cancel(Order $order): void
{
echo "Cannot cancel: already shipped" . PHP_EOL;
}
}
// 已签收状态
class DeliveredState implements OrderState
{
public function pay(Order $order): void
{
echo "Already paid" . PHP_EOL;
}
public function ship(Order $order): void
{
echo "Already shipped" . PHP_EOL;
}
public function deliver(Order $order): void
{
echo "Already delivered" . PHP_EOL;
}
public function cancel(Order $order): void
{
echo "Cannot cancel: already delivered" . PHP_EOL;
}
}
// 已取消状态
class CancelledState implements OrderState
{
public function pay(Order $order): void
{
echo "Cannot pay: order cancelled" . PHP_EOL;
}
public function ship(Order $order): void
{
echo "Cannot ship: order cancelled" . PHP_EOL;
}
public function deliver(Order $order): void
{
echo "Cannot deliver: order cancelled" . PHP_EOL;
}
public function cancel(Order $order): void
{
echo "Already cancelled" . PHP_EOL;
}
}
// 使用
$order = new Order("ORD-001");
$order->ship(); // Cannot ship: order not paid
$order->pay(); // Payment received
$order->ship(); // Shipped
$order->cancel(); // Cannot cancel: already shipped
$order->deliver(); // Delivered2.3 实际案例:红绿灯
php
<?php
// 交通灯状态
interface TrafficLightState
{
public function next(TrafficLight $light): void;
public function getColor(): string;
public function getDuration(): int;
}
class RedLight implements TrafficLightState
{
public function getColor(): string
{
return "RED";
}
public function getDuration(): int
{
return 30;
}
public function next(TrafficLight $light): void
{
echo "RED → GREEN" . PHP_EOL;
$light->setState(new GreenLight());
}
}
class GreenLight implements TrafficLightState
{
public function getColor(): string
{
return "GREEN";
}
public function getDuration(): int
{
return 25;
}
public function next(TrafficLight $light): void
{
echo "GREEN → YELLOW" . PHP_EOL;
$light->setState(new YellowLight());
}
}
class YellowLight implements TrafficLightState
{
public function getColor(): string
{
return "YELLOW";
}
public function getDuration(): int
{
return 5;
}
public function next(TrafficLight $light): void
{
echo "YELLOW → RED" . PHP_EOL;
$light->setState(new RedLight());
}
}
class TrafficLight
{
private TrafficLightState $state;
public function __construct()
{
$this->state = new RedLight();
}
public function setState(TrafficLightState $state): void
{
$this->state = $state;
}
public function change(): void
{
echo "Current: {$this->state->getColor()} ({$this->state->getDuration()}s)" . PHP_EOL;
$this->state->next($this);
}
}
// 使用
$light = new TrafficLight();
$light->change(); // RED → GREEN
$light->change(); // GREEN → YELLOW
$light->change(); // YELLOW → RED三、适用场景
| 场景 | 说明 |
|---|---|
| 状态机 | 订单、工单、审批流 |
| 游戏角色状态 | 站立、跑步、跳跃、攻击 |
| 自动售货机 | 投币、选择商品、出货 |
| 文档审批 | 草稿、审核中、已发布 |
| 网络连接 | 连接中、已连接、断开 |
四、优缺点分析
| 优点 | 缺点 |
|---|---|
| 消除条件语句 | 类数量增加 |
| 状态切换清晰 | 状态多时复杂 |
| 符合开闭原则 | 状态间耦合 |
| 行为与状态绑定 | 可能导致状态爆炸 |
| 易于扩展新状态 | 简单状态过度设计 |
五、常见踩坑与问题排查
5.1 状态间循环依赖
php
<?php
// 问题:A 依赖 B,B 依赖 A,可能导致循环
class StateA implements State
{
public function handle(Context $context): void
{
$context->setState(new StateB()); // A → B
}
}
class StateB implements State
{
public function handle(Context $context): void
{
$context->setState(new StateA()); // B → A
}
}
// 解决:明确状态转换规则,必要时使用状态转换表5.2 状态爆炸
php
<?php
// 问题:状态太多,难以维护
// 解决:使用状态转换表或状态机库
// 状态转换表(PHP 原生实现)
$transitions = [
'pending' => ['pay' => 'paid', 'cancel' => 'cancelled'],
'paid' => ['ship' => 'shipped', 'cancel' => 'cancelled'],
'shipped' => ['deliver' => 'delivered'],
'delivered' => [],
'cancelled' => [],
];
// 简单状态机
class OrderMachine
{
private string $current = 'pending';
public function __construct(array $transitions)
{
$this->transitions = $transitions;
}
public function trigger(string $event): void
{
$next = $this->transitions[$this->current][$event] ?? null;
if ($next !== null) {
echo "{$this->current} → {$next}" . PHP_EOL;
$this->current = $next;
} else {
echo "Cannot {$event} in state {$this->current}" . PHP_EOL;
}
}
}六、优化方案与进阶
6.1 状态转换表
php
<?php
// 使用表驱动代替状态类
class TableStateMachine
{
private string $current = 'pending';
/** @var array<string, array<string, string>> */
private array $transitions = [
'pending' => ['pay' => 'paid', 'cancel' => 'cancelled'],
'paid' => ['ship' => 'shipped', 'cancel' => 'cancelled'],
'shipped' => ['deliver' => 'delivered'],
'delivered' => [],
'cancelled' => [],
];
public function trigger(string $event): void
{
$next = $this->transitions[$this->current][$event] ?? null;
if ($next !== null) {
echo "{$this->current} → {$next}" . PHP_EOL;
$this->current = $next;
} else {
echo "Cannot {$event} in state {$this->current}" . PHP_EOL;
}
}
}6.2 共享状态对象
php
<?php
// 单例状态:避免频繁创建状态对象
final class States
{
private function __construct() {}
public static PendingState $pending;
public static PaidState $paid;
public static ShippedState $shipped;
public static DeliveredState $delivered;
public static CancelledState $cancelled;
public static function init(): void
{
self::$pending = new PendingState();
self::$paid = new PaidState();
self::$shipped = new ShippedState();
self::$delivered = new DeliveredState();
self::$cancelled = new CancelledState();
}
}
// 初始化
States::init();
class Order
{
private OrderState $state = States::$pending;
}七、全文总结
状态模式的核心是 将状态逻辑封装为独立对象,状态切换时改变行为。
核心要点:
- 上下文持有当前状态,委托状态处理请求
- 每个状态独立类,避免条件语句
- 适用于状态机、工作流、游戏等场景
- 与策略模式相似,但状态模式关注状态转换
- 可用状态转换表、状态机库简化复杂状态
