跳到主要内容

状态模式

状态模式通过将对象所属的状态抽象成类,并利用继承和多态性质来实现状态的切换,从而使得对象在不同状态下能够动态地表现出不同的行为。

结构

状态模式示意图状态模式示意图

状态模式中,上下文(Context)对象为客户端提供了一个接口,用于在不同状态下调用不同的行为。状态(State)接口定义了所有具体状态类的通用方法。具体状态(Concrete State)类实现了状态接口,并定义了在该状态下的行为。

对客户端而言,调用上下文对象的方法就像调用一个普通的对象一样,但得益于多态性质,上下文对象能够根据状态的不同表现出不同的行为。

应用场景

  • 当对象需要根据自身当前状态进行不同行为,且状态的数量非常多、与状态相关的代码会频繁变更时
  • 当对象的行为取决于其状态,且需要在运行时动态改变状态时

优缺点

优点

  • 单一职责原则:状态与状态对应的行为被封装在同一类中,而与状态无关的行为被封装在其他类中。
  • 开闭原则:可以在不修改上下文的情况下扩展状态与对应的行为。
  • 消除大量条件语句。

缺点

  • 在状态、行为较少时没有必要使用状态模式。

代码示例

下面通过一个自动售货机的例子来展示状态模式的实现。

VendingMachine上下文对象,它维护着当前的状态(_state)、商品库存和投入的硬币数量。它将所有与状态相关的操作(如 insert_coin, select_item)委托给当前的状态对象来处理。

State 是一个抽象基类,定义了所有具体状态必须实现的接口。NoCoinState, HasCoinState, SoldState, 和 SoldOutState具体的状态类。每个类都实现了在特定状态下,当用户执行操作时应该发生的行为。

关键在于,状态的转换是由状态对象自身管理的。例如,在 NoCoinState 下,当用户投入硬币(insert_coin)时,状态会切换到 HasCoinState。在 HasCoinState 下,当用户选择商品(select_item)且硬币足够时,状态会切换到 SoldState

这种设计将每种状态的行为和转换逻辑封装在各自的类中,使得 VendingMachine 类本身不包含任何复杂的 if/else 逻辑来判断当前状态,从而使代码更加清晰和易于维护。

备注

值得注意的是,这里的 change_state 方法实际上是切换状态和状态的工厂方法的结合。这种方式可以避免在客户端代码中直接创建状态对象,从而降低了耦合度。

同时,该方法的类型标注中使用了 Literal 类型用于指定参数的取值范围。由此可以编写更加类型安全的代码。在更高版本的 Python 中,还可以对类型使用 * 运算符解包,以避免重复书写。

此外,为实现此机制还声明了一个字符串到状态类型的映射,以便在 change_state 方法中根据字符串创建对应的状态对象。该字典被声明为 ClassVar 以避免 dataclass 将其视为实例变量。