起步
状态模式属于行为型,旨在解决“反复出现”的设计问题。
“反复出现”你可以理解为一类状态会在程序运行过程中反复出现。并且,在不同状态下,状态持有者会表现出不同的行为。
实现一个交通信号灯
现在,假设我们需要开发一个交通信号灯的功能,要求是:
- 信号灯在不同状态下,呈现不同的颜色:红、黄、绿;
- 不同的颜色,持续时间不同;
- 持续时间结束后,按照一定的规则切换状态:红 -> 绿 -> 黄 -> 红 ...
第一步,很快可以想到用枚举定义这种三状态:
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() # 委托状态的行为
还有一点是我个人的看法:状态模式中,状态与状态之间存在固定的转换关系。策略模式中,策略和策略之间不存在转换关系。
还不快抢沙发