状态模式的核心是将状态相关分支逻辑封装到独立类中,主类仅负责委托;当类中出现大量基于self.state的条件判断且各分支逻辑复杂时应采用该模式。

状态模式的核心作用不是“消除分支”,而是把分支逻辑拆到独立类里
直接用 if/elif/else 判断状态并执行不同行为,代码会随着状态增多迅速腐化。状态模式不消灭分支,而是把每个状态的响应逻辑封装进单独类中,让主类只负责委托,不掺和具体实现。
关键判断点:当你的类里出现大量与 self.state 相关的条件判断(比如 if self.state == "pending": ... elif self.state == "processing": ...),且每个分支内有较复杂的操作或后续状态变更逻辑,就该考虑状态模式了。
- 状态类必须实现统一接口(如
handle()、transition_to()) - 主类(Context)持有当前状态对象引用,所有状态相关行为都转发给它
- 状态切换由状态自身或 Context 控制,但推荐由状态类决定“下一步该变为什么”——更符合状态机语义
如何定义状态接口和基础状态类
Python 没有 interface 关键字,用抽象基类或仅靠约定即可。推荐用 abc.ABC 明确契约,避免漏实现方法。
from abc import ABC, abstractmethod
<p>class State(ABC):
def <strong>init</strong>(self, context):
self.context = context # 允许状态间触发上下文变更</p><pre class="brush:php;toolbar:false;">@abstractmethod
def handle(self):
pass
@abstractmethod
def on_event_a(self):
pass
@abstractmethod
def on_event_b(self):
pass
注意:context 引用是双向通信的关键——比如某个状态处理完事件后,需要调用 self.context.set_state(NewState(self.context)) 来切换自身。没有它,状态类就成了纯函数,失去“驱动流转”的能力。
为什么不能在状态类里直接修改 context.state
常见错误是让状态类直接写 self.context.state = NewState(...)。这破坏了封装,也绕过了可能存在的状态变更校验逻辑(比如某些状态不允许跳转)。
- 正确做法:Context 提供
set_state(new_state)方法,内部可做日志、前置检查、清理旧状态资源等 - 状态类通过
self.context.set_state(...)发起变更,而不是直接赋值 - 如果状态切换规则复杂(如依赖外部条件),建议把决策权交给 Context 的
transition()方法,状态类只返回目标状态类型或标识符
否则你会在多个状态类里重复写相同的校验逻辑,或者某天加个新限制时漏改某个状态类。
实际项目中容易被忽略的初始化和生命周期问题
状态模式不是“写完几个状态类就完事”。真实场景下,状态往往关联资源(如数据库连接、文件句柄、线程锁),而 Python 的垃圾回收不保证析构时机。
- 状态切换前,旧状态的
exit()或cleanup()方法应被显式调用(不要依赖__del__) - Context 初始化时,必须确保第一个状态被正确构造并设为当前状态——漏掉这步会导致
AttributeError: 'NoneType' object has no attribute 'handle' - 调试时打印当前状态类型(
type(context._state).__name__)比打印字符串值更可靠,避免字符串拼写错误导致静默失败
最麻烦的是异步或并发场景:多个线程同时触发事件,可能造成状态覆盖或中间态丢失。这时得在 set_state 内加锁,或改用原子性更强的状态机库(如 transitions),而非手写模式。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











