本文目录导读:

我来详细解释如何使用状态模式重构复杂条件逻辑,并提供实际示例。
什么是状态模式
状态模式允许对象在内部状态改变时改变其行为,看起来就像修改了对象的类一样,主要解决复杂条件判断(if-else / switch)导致的代码臃肿问题。
问题场景示例
原始代码(使用大量条件判断)
<?php
class Order
{
const STATUS_NEW = 'new';
const STATUS_PAID = 'paid';
const STATUS_SHIPPED = 'shipped';
const STATUS_COMPLETED = 'completed';
const STATUS_CANCELLED = 'cancelled';
private string $status;
public function __construct()
{
$this->status = self::STATUS_NEW;
}
// 支付操作
public function pay(): void
{
if ($this->status === self::STATUS_NEW) {
echo "订单支付成功\n";
$this->status = self::STATUS_PAID;
} elseif ($this->status === self::STATUS_PAID) {
echo "订单已支付,不能重复支付\n";
} elseif ($this->status === self::STATUS_SHIPPED) {
echo "订单已发货,不能支付\n";
} elseif ($this->status === self::STATUS_COMPLETED) {
echo "订单已完成,不能支付\n";
} elseif ($this->status === self::STATUS_CANCELLED) {
echo "订单已取消,不能支付\n";
} else {
throw new \Exception("未知状态");
}
}
// 发货操作(继续膨胀...)
public function ship(): void
{
if ($this->status === self::STATUS_PAID) {
echo "订单发货成功\n";
$this->status = self::STATUS_SHIPPED;
} elseif ($this->status === self::STATUS_NEW) {
echo "订单未支付,不能发货\n";
} elseif ($this->status === self::STATUS_SHIPPED) {
echo "订单已发货,不能重复发货\n";
} elseif ($this->status === self::STATUS_COMPLETED) {
echo "订单已完成,不能发货\n";
} elseif ($this->status === self::STATUS_CANCELLED) {
echo "订单已取消,不能发货\n";
} else {
throw new \Exception("未知状态");
}
}
// 完成操作(继续膨胀...)
public function complete(): void
{
if ($this->status === self::STATUS_SHIPPED) {
echo "订单完成\n";
$this->status = self::STATUS_COMPLETED;
} elseif ($this->status === self::STATUS_NEW) {
echo "订单未发货,不能完成\n";
} elseif ($this->status === self::STATUS_PAID) {
echo "订单未发货,不能完成\n";
} elseif ($this->status === self::STATUS_COMPLETED) {
echo "订单已完成,不能重复完成\n";
} elseif ($this->status === self::STATUS_CANCELLED) {
echo "订单已取消,不能完成\n";
} else {
throw new \Exception("未知状态");
}
}
}
问题:
- 每个方法都有大量if-else嵌套
- 添加新状态需要修改所有方法
- 代码可读性差,维护困难
状态模式重构
定义状态接口
<?php
// 订单状态接口
interface OrderStateInterface
{
// 执行支付操作
public function pay(OrderContext $order): void;
// 执行发货操作
public function ship(OrderContext $order): void;
// 执行完成操作
public function complete(OrderContext $order): void;
// 执行取消操作
public function cancel(OrderContext $order): void;
// 获取当前状态名
public function getStatusName(): string;
}
实现具体状态类
<?php
// 新订单状态
class NewOrderState implements OrderStateInterface
{
public function pay(OrderContext $order): void
{
echo "订单支付成功\n";
$order->setState(new PaidOrderState());
}
public function ship(OrderContext $order): void
{
echo "订单未支付,不能发货\n";
}
public function complete(OrderContext $order): void
{
echo "订单未发货,不能完成\n";
}
public function cancel(OrderContext $order): void
{
echo "订单取消成功\n";
$order->setState(new CancelledOrderState());
}
public function getStatusName(): string
{
return "新订单";
}
}
// 已支付状态
class PaidOrderState implements OrderStateInterface
{
public function pay(OrderContext $order): void
{
echo "订单已支付,不能重复支付\n";
}
public function ship(OrderContext $order): void
{
echo "订单发货成功\n";
$order->setState(new ShippedOrderState());
}
public function complete(OrderContext $order): void
{
echo "订单未发货,不能完成\n";
}
public function cancel(OrderContext $order): void
{
echo "订单已支付,不能取消\n";
}
public function getStatusName(): string
{
return "已支付";
}
}
// 已发货状态
class ShippedOrderState implements OrderStateInterface
{
public function pay(OrderContext $order): void
{
echo "订单已发货,不能支付\n";
}
public function ship(OrderContext $order): void
{
echo "订单已发货,不能重复发货\n";
}
public function complete(OrderContext $order): void
{
echo "订单完成成功\n";
$order->setState(new CompletedOrderState());
}
public function cancel(OrderContext $order): void
{
echo "订单已发货,不能取消\n";
}
public function getStatusName(): string
{
return "已发货";
}
}
// 已完成状态
class CompletedOrderState implements OrderStateInterface
{
public function pay(OrderContext $order): void
{
echo "订单已完成,不能支付\n";
}
public function ship(OrderContext $order): void
{
echo "订单已完成,不能发货\n";
}
public function complete(OrderContext $order): void
{
echo "订单已完成,不能重复完成\n";
}
public function cancel(OrderContext $order): void
{
echo "订单已完成,不能取消\n";
}
public function getStatusName(): string
{
return "已完成";
}
}
// 已取消状态
class CancelledOrderState implements OrderStateInterface
{
public function pay(OrderContext $order): void
{
echo "订单已取消,不能支付\n";
}
public function ship(OrderContext $order): void
{
echo "订单已取消,不能发货\n";
}
public function complete(OrderContext $order): void
{
echo "订单已取消,不能完成\n";
}
public function cancel(OrderContext $order): void
{
echo "订单已取消,不能重复取消\n";
}
public function getStatusName(): string
{
return "已取消";
}
}
创建上下文类
<?php
class OrderContext
{
private OrderStateInterface $state;
// 订单数据
private array $orderData = [];
public function __construct()
{
$this->state = new NewOrderState();
}
public function setState(OrderStateInterface $state): void
{
$this->state = $state;
}
// 委托操作给当前状态
public function pay(): void
{
$this->state->pay($this);
}
public function ship(): void
{
$this->state->ship($this);
}
public function complete(): void
{
$this->state->complete($this);
}
public function cancel(): void
{
$this->state->cancel($this);
}
// 获取当前状态
public function getStatusName(): string
{
return $this->state->getStatusName();
}
// 设置订单数据
public function setData(string $key, $value): void
{
$this->orderData[$key] = $value;
}
public function getData(string $key)
{
return $this->orderData[$key] ?? null;
}
}
使用示例
<?php
// 使用重构后的代码
$order = new OrderContext();
$order->setData('order_no', '2024001');
echo "初始状态:" . $order->getStatusName() . "\n";
// 模拟订单流程
$order->pay(); // 输出:订单支付成功
echo "当前状态:" . $order->getStatusName() . "\n";
$order->pay(); // 输出:订单已支付,不能重复支付
$order->ship(); // 输出:订单发货成功
echo "当前状态:" . $order->getStatusName() . "\n";
$order->complete(); // 输出:订单完成成功
echo "当前状态:" . $order->getStatusName() . "\n";
// 测试取消场景
$order2 = new OrderContext();
$order2->pay();
echo "当前状态:" . $order2->getStatusName() . "\n";
$order2->cancel(); // 输出:订单已支付,不能取消
$order2->ship();
echo "当前状态:" . $order2->getStatusName() . "\n";
$order2->cancel(); // 输出:订单已发货,不能取消
状态模式的核心优点
开闭原则
- 新增状态时只需添加新状态类,无需修改现有代码
- 完全符合"对扩展开放,对修改关闭"
消除条件判断
- 移除大量的if-else/switch语句
- 状态行为被分散到各自的状态类中
代码组织清晰
- 每个状态的行为集中在一个类中
- 状态转换逻辑单一明确
状态一致性
- 状态转换被封装在状态类中
- 避免了非法状态转换
状态模式的缺点和注意事项
缺点
- 类数量增多:每个状态都需要一个类
- 状态转换分散:转换逻辑分布在各个状态类中
使用建议
// 1. 如果状态不多且简单,直接使用enum + switch即可
enum OrderStatus {
case NEW;
case PAID;
case SHIPPED;
}
// 简单场景不需要状态模式
function checkOrderStatus(OrderStatus $status) {
switch ($status) {
case OrderStatus::NEW:
// do something
break;
case OrderStatus::PAID:
// do something
break;
}
}
// 2. 当状态转换逻辑复杂、每个状态有大量行为时,使用状态模式
// 3. 状态数量较多且经常变化时,考虑状态模式
进阶:结合工厂模式
<?php
// 状态工厂(可选,用于从数据库恢复状态)
class OrderStateFactory
{
public static function createState(string $status): OrderStateInterface
{
return match ($status) {
'new' => new NewOrderState(),
'paid' => new PaidOrderState(),
'shipped' => new ShippedOrderState(),
'completed' => new CompletedOrderState(),
'cancelled' => new CancelledOrderState(),
default => throw new \InvalidArgumentException("未知状态: {$status}"),
};
}
}
// 增强的OrderContext
class AdvancedOrderContext
{
private OrderStateInterface $state;
public function restoreState(string $status): void
{
$this->state = OrderStateFactory::createState($status);
}
// 保存状态(用于持久化)
public function getCurrentState(): string
{
return $this->state->getStatusName();
}
}
状态模式最适合:
- 操作包含庞大的多分支条件语句
- 对象的行为依赖于它的状态,并随着状态改变而改变
- 状态转换规则复杂且需要统一管理
通过这个重构示例,可以看到状态模式如何显著提升代码的可维护性和可扩展性,关键在于将状态和对应行为封装为独立对象,使状态转换变得显式且安全。