State 状态模式

设计模式 2020-12-30 5221 字 934 浏览 点赞

起步

状态模式属于行为型,旨在解决“反复出现”的设计问题。

“反复出现”你可以理解为一类状态会在程序运行过程中反复出现。并且,在不同状态下,状态持有者会表现出不同的行为。

实现一个交通信号灯

现在,假设我们需要开发一个交通信号灯的功能,要求是:

  1. 信号灯在不同状态下,呈现不同的颜色:红、黄、绿;
  2. 不同的颜色,持续时间不同;
  3. 持续时间结束后,按照一定的规则切换状态:红 -> 绿 -> 黄 -> 红 ...

第一步,很快可以想到用枚举定义这种三状态:

from enum import Enum

class State(str, Enum):
    red = "red"
    green = "green"
    yellow = "yellow"

第二步,实现信号灯。根据需求,可将其简化为三个方法一个属性:

  • 属性:state,用来存放当前信号灯的状态。
  • 方法 1:color(),根据当前状态,显示当前颜色。
  • 方法 2:duration(),根据当前状态,设置持续时间。
  • 方法 3:change(),根据当前状态,按规则切换到目标状态。

信号灯的实现如下:

import time

class TrafficLight(object):
    def __init__(self) -> None:
        self.state = None

    def color(self) -> str:
        """ 显示颜色 """
        if self.state is None:
            return ""
        return self.state.value
        
    def duration(self) -> int:
        """ 获取持续时间 """
        if self.state == State.red:
            return 6
        elif self.state == State.green:
            return 4
        elif self.state == State.yellow:
            return 2
        return 0
    
    def change(self):
        """ 切换状态 """
        if self.state == State.red:
            self.state = State.green  # 红 -> 绿
        elif self.state == State.green:
            self.state = State.yellow  # 绿 -> 黄
        elif self.state == State.yellow:
            self.state = State.red    # 黄 -> 红
        else:  # 没有状态, 默认为“红”  
            self.state = State.red
        
    def on(self):
        self.change()
        while True:
            color, duration = self.color(), self.duration()
            print(f"state: {color}, last: {duration}s ...")
            time.sleep(duration)
            self.change()

if __name__ == "__main__":
    light = TrafficLight()
    light.on()

执行结果:

state: red, last: 6s ...
state: green, last: 4s ...
state: yello, last: 2s ...
state: red, last: 6s ...
state: green, last: 4s ...
...

什么是状态模式

第一个问题来了:上述实现方式,可以认为是状态模式吗?答:不是。

第二个问题接踵而至:不同 state 下,color、duration、change 表现的行为不同,为什么就不是状态模式呢?

要解答第二个问题,我们就需要知道什么是状态模式。看看维基百科怎么说:

  • Define separate (state) objects that encapsulate state-specific behavior for each state. That is, define an interface (state) for performing state-specific behavior, and define classes that implement the interface for each state.
  • A class delegates state-specific behavior to its current state object instead of implementing state-specific behavior directly.

想表达的意思就是:

  • 定义不同的状态,在状态中实现特定的行为。
  • 要委托当前状态的行为,不要自己实现状态的行为。

看看之前的代码,State.red、State.green、State.yellow 有各自的行为吗?没有。TrafficLight.color、TrafficLight.duration、TrafficLight.change 是委托状态的行为吗?不是,是自己实现的。

这种自己去实现状态的行为的方式是不灵活的,如果现在要增加一个状态,势必得修改 TrafficLight 类。可 TrafficLight 作为客户端,新增状态和它有啥关系。不应该去修改它。

状态模式下的信号灯

按照状态模式的要求,我们需要定义 red、green、yellow 三个状态,并为其实现各自的行为:

from abc import abstractmethod

class State(object):
    @abstractmethod
    def color(self) -> str:
        """ 显示颜色 """
        pass
    
    @abstractmethod
    def duration(self) -> int:
        """ 持续时间 """
        pass
    
    @abstractmethod
    def change(self, light):
        """ 切换状态 """
        pass

class Red(State):
    def color(self) -> str:
        return "red"

    def duration(self) -> int:
        return 6
    
    def change(self, light):
        light.state = Green()  # 红 -> 绿

class Green(State):
    def color(self) -> str:
        return "green"

    def duration(self) -> int:
        return 4
    
    def change(self, light):
        light.state = Yellow()  # 绿 -> 黄

class Yellow(State):
    def color(self) -> str:
        return "yello"

    def duration(self) -> int:
        return 2
    
    def change(self, light):  # 黄 -> 红
        light.state = Red()

然后是 TrafficLight,将它的行为委托给当前状态(self.state):

import time

class TrafficLight(object):
    def __init__(self) -> None:
        self.state = None
    
    def change(self):
        if self.state is None:
            self.state = Red()
            return
        self.state.change(self)  # 委托状态的行为
    
    def color(self) -> str:
        return self.state.color()  # 委托状态的行为
    
    def duration(self) -> int:
        return self.state.duration()  # 委托状态的行为
    
    def on(self):
        # 在不同状态下,color()、duration()、change() 行为不同
        self.change()
        while True:
            color, duration = self.color(), self.duration()
            print(f"state: {color}, last: {duration}s ...")
            time.sleep(duration)
            self.change()

if __name__ == "__main__":
    light = TrafficLight()
    light.on()

总结

乍一看,状态模式和策略模式还蛮相似的,甚至是,信号灯这个需求可以用策略模式实现。但区别在于:策略模式中客户端一般不需要委托选中的策略,而是直接拿来使用;但状态模式下,使用者会把自己的行为委托给状态。类似这样:

class TrafficLight(object):
    def color(self) -> str:
        return self.state.color()  # 委托状态的行为

还有一点是我个人的看法:状态模式中,状态与状态之间存在固定的转换关系。策略模式中,策略和策略之间不存在转换关系。

参考



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论