Python 中的状态模式(State Pattern)
状态模式是一种行为型设计模式,其核心目的是:
允许一个对象在其内部状态改变时改变它的行为。从外部看,就像对象改变了其类一样。
形象比喻:就像人的心情——开心时会笑、生气时会生气、疲惫时会休息。同一人(上下文对象),因为内部状态不同,行为完全不同。
状态模式的优点
- 消除庞大的条件判断语句(if-elif-else)
- 将每个状态的行为局部化到各自的类中,符合单一职责原则
- 状态转换显式化:更容易理解和管理状态迁移规则
- 易于扩展新状态:新增状态只需新增类,不修改原有代码
- 对象行为随状态动态变化,非常灵活
典型应用场景
- 订单状态机(待支付 → 已支付 → 发货中 → 已完成 → 退款)
- TCP 连接状态(LISTEN → SYN_SENT → ESTABLISHED → CLOSED)
- 游戏角色状态(正常、攻击、中毒、眩晕、无敌)
- 工作流引擎(审批流程)
- UI 控件状态(启用、禁用、悬停、焦点)
Python 实现示例:订单状态机
from abc import ABC, abstractmethod
# 上下文(Context):持有当前状态
class Order:
def __init__(self):
self.state = PendingPaymentState(self) # 初始状态:待支付
self.order_id = "ORD12345"
def set_state(self, state):
self.state = state
print(f"订单 {self.order_id} 状态变更为: {state.__class__.__name__}")
def pay(self):
self.state.pay()
def ship(self):
self.state.ship()
def complete(self):
self.state.complete()
def refund(self):
self.state.refund()
def __str__(self):
return f"订单 {self.order_id} [{self.state.__class__.__name__}]"
# 抽象状态(State)
class OrderState(ABC):
def __init__(self, order: Order):
self.order = order
@abstractmethod
def pay(self):
pass
@abstractmethod
def ship(self):
pass
@abstractmethod
def complete(self):
pass
@abstractmethod
def refund(self):
pass
# 具体状态1:待支付
class PendingPaymentState(OrderState):
def pay(self):
print("支付成功!")
self.order.set_state(PaidState(self.order))
def ship(self):
print("错误:订单尚未支付,无法发货")
def complete(self):
print("错误:订单尚未支付,无法完成")
def refund(self):
print("错误:订单尚未支付,无法退款")
# 具体状态2:已支付
class PaidState(OrderState):
def pay(self):
print("错误:订单已支付,无需重复支付")
def ship(self):
print("开始发货...")
self.order.set_state(ShippedState(self.order))
def complete(self):
print("错误:订单尚未发货,无法完成")
def refund(self):
print("退款处理中...")
self.order.set_state(RefundingState(self.order))
# 具体状态3:已发货
class ShippedState(OrderState):
def pay(self):
print("错误:订单已支付")
def ship(self):
print("错误:订单已发货,无需重复发货")
def complete(self):
print("订单已签收,交易完成!")
self.order.set_state(CompletedState(self.order))
def refund(self):
print("发货后退款需审核...")
# 可以转到另一个状态,或保持当前
# 具体状态4:已完成
class CompletedState(OrderState):
def pay(self): print("订单已完成")
def ship(self): print("订单已完成")
def complete(self): print("订单已完成")
def refund(self): print("已完成订单不支持退款")
# 具体状态5:退款中
class RefundingState(OrderState):
def pay(self): print("退款中...")
def ship(self): print("退款中...")
def complete(self): print("退款中...")
def refund(self): print("退款处理完成")
# 可以转到 RefundedState 等
# 客户端使用
if __name__ == "__main__":
order = Order()
print(order)
order.pay() # → PaidState
print(order)
order.ship() # → ShippedState
print(order)
order.complete() # → CompletedState
print(order)
print("\n--- 尝试非法操作 ---")
order.pay() # 已支付,再支付报错
order.refund() # 已完成,不支持退款
输出:
订单 ORD12345 [PendingPaymentState]
支付成功!
订单 ORD12345 状态变更为: PaidState
订单 ORD12345 [PaidState]
开始发货...
订单 ORD12345 状态变更为: ShippedState
订单 ORD12345 [ShippedState]
订单已签收,交易完成!
订单 ORD12345 状态变更为: CompletedState
订单 ORD12345 [CompletedState]
--- 尝试非法操作 ---
错误:订单已支付,无需重复支付
已完成订单不支持退款
Pythonic 简化版:使用字典 + 函数(适合简单状态机)
当状态不多、行为简单时,可以避免创建多个类:
class SimpleOrder:
def __init__(self):
self.state = "pending"
self.states = {
"pending": {
"pay": lambda: self.transition("paid"),
"ship": lambda: print("不能发货"),
},
"paid": {
"pay": lambda: print("已支付"),
"ship": lambda: self.transition("shipped"),
"refund": lambda: self.transition("refunding"),
},
"shipped": {
"complete": lambda: self.transition("completed"),
},
"completed": {
"pay": lambda: print("已完成"),
"refund": lambda: print("不支持退款"),
},
}
def transition(self, new_state):
print(f"状态变更为: {new_state}")
self.state = new_state
def action(self, cmd):
handler = self.states.get(self.state, {}).get(cmd)
if handler:
handler()
else:
print(f"当前状态 {self.state} 不支持操作 {cmd}")
# 使用
order = SimpleOrder()
order.action("pay")
order.action("ship")
order.action("complete")
状态模式结构总结
| 角色 | 说明 |
|---|---|
| Context | 上下文(Order),持有当前状态引用 |
| State | 抽象状态接口(OrderState) |
| ConcreteState | 具体状态类(PendingPaymentState 等) |
状态模式 vs 策略模式对比
| 模式 | 目的 | 切换时机 | 典型场景 |
|---|---|---|---|
| 状态 | 行为随内部状态改变 | 运行时自动切换 | 订单、TCP、游戏角色 |
| 策略 | 行为算法可外部替换 | 外部手动设置 | 支付方式、排序算法 |
关键区别:状态模式中,状态对象自己决定下一个状态(内部驱动);策略模式中,客户端决定使用哪个策略(外部驱动)。
Python 中的实用建议
- 复杂状态机:使用类实现状态模式(清晰、可维护)
- 简单状态:用枚举 + 字典映射行为
- 结合枚举更安全:
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
COMPLETED = "completed"
class Order:
def __init__(self):
self.status = OrderStatus.PENDING
- 真实项目中常见:
- Django 的模型状态字段 + 状态迁移
- Celery 任务状态
- 游戏引擎中的角色状态系统
注意事项
- 状态类不要太多(否则维护困难)
- 确保所有状态都实现了完整接口(避免遗漏操作)
- 状态转换逻辑要集中管理(避免分散在多个地方)
状态模式是构建复杂状态机的最佳实践之一,能让状态逻辑清晰、可扩展、可测试。
如果你想看更高级的例子(如游戏角色多状态叠加、带超时自动转状态、工作流引擎实现),或者如何与备忘录模式结合实现状态快照,欢迎继续问!