状态模式应将状态行为解耦,通过orderstate接口定义pay/cancel/ship等有限操作,各状态类仅实现合法动作;状态切换由状态自身方法返回新实例,初始化与非法迁移需统一兜底处理。

Switch语句太多,说明行为与状态耦合太紧
PHP里一堆 switch ($status) 套着十几个 case,每个分支还调用不同方法、改不同属性——这不是逻辑复杂,是状态职责没拆开。状态模式不是为“炫技”而加,而是当 $status 的取值直接影响对象行为(比如订单从 'pending' 到 'shipped' 后,cancel() 和 ship() 的合法性、副作用都变了),就该让状态自己决定“能干什么”。
State接口必须只暴露上下文需要委托的行为
别一上来就定义 handleX()、handleY()、handleZ()……接口膨胀后,每个具体状态类都要实现一堆空方法。真正要做的,是看上下文类(比如 Order)当前有哪些操作会被状态拦截——通常就 2~4 个,例如:pay()、cancel()、ship()。把这些方法抽成接口:
interface OrderState
{
public function pay(Order $order): void;
public function cancel(Order $order): void;
public function ship(Order $order): void;
}
每个具体状态类只实现它“允许”的动作,其余抛出 DomainException 或静默忽略(视业务而定)。
状态切换不能由外部直接 new XxxState(),得封装在状态内部
常见错误是写成:$order->state = new ShippedState(); —— 这等于把状态迁移逻辑泄露到业务代码里,下次加个新状态就得全局搜 new。正确做法是:状态类自己提供迁移方法,并返回新状态实例:
-
PendingState::pay()返回new PaidState() -
PaidState::ship()返回new ShippedState() -
ShippedState::cancel()返回new CancelledState()(或抛异常)
上下文的 pay() 方法就变成:$this->state = $this->state->pay($this);。这样状态流转规则全在状态类里,增删改查都在一个地方。
别忘了处理状态初始化和非法迁移兜底
新订单创建时,$this->state 该设谁?别写死 new PendingState(),用工厂或静态构造器更可控;更关键的是,当某个状态收到不支持的操作(比如对 ShippedState 调用 pay()),不要让它静默失败——记录日志、抛带上下文的异常(如 InvalidTransitionException("Cannot pay a shipped order")),否则线上问题会卡在“点了没反应”,排查时翻半天 switch 才发现漏了 case。
状态模式解决的从来不是“怎么写更短”,而是“哪个类该为某次状态变更负责”。一旦把 switch 拆进独立类,后续加状态、改规则、写单元测试,就不用再碰主流程代码了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











