大家好,今天给大家介绍一个新的设计模式,这个设计模式非常重要,在我们日常的开发工作当中经常使用。它就是大名鼎鼎的状态机模式。
状态机模式非常适合用在复杂的流程或者是系统当中,可以方便我们对系统的某一个状态进行抽象,这会让我们编码具有更强的可读性以及延展性。
有向图与DAG
首先和大家解释一下状态机当中这个状态的概念,这里的状态指的是我们系统或者是流程当中的某一个状态。我用我之前做过的一个活动系统来给大家举一个例子。
比如我们现在要在网上举办一些活动,然后吸引用户来参与。但是在用户来参与活动的过程当中其实有很多的状态需要判断,比如说我们首先要判断用户是否已经登录了。如果登录了,还需要判断用户之前是否报名过,如果已经报名了,还需要判断活动是否开始了等等。
那么,我们就可以抽象出很多的状态。比如是否登录、是否报名、未登录等等这些都是状态。这些状态之间可以通过一些条件进行转移,比如在初始状态当中,通过判断用户是否登录选择转移到未登录状态或者是报名判断的状态上。我们把这其中的逻辑抽象出来,可以得到这么一张有向无环图。
几乎所有的固定流程都可以抽象出这么一张图来,这种图一般被缩写成DAG(Directed Acyclic Graph)。如果不用状态机的话,那么我们需要编写大量的代码来进行判断。就拿上述的这个逻辑举例,我们需要至少4层if嵌套的逻辑判断来实现这么一个流程。
如果通过if判断来实现的话,那么面临的一个问题就是这个流程是固定的。如果临时需要改动,那么必须要修改代码,而我们知道不管大小公司,发布代码都是有严格的规范的,是不能随意发布的。而使用状态机主要解决的就是这个问题,可以把流程做成可配置的,如果需要临时修改,只需要修改状态机的对应配置即可,可以规避掉代码层面的修改。
状态与状态机
理解了DAG之后,我们再来看看状态机的定义和解释。
状态机的官方定义是:
The intent of the STATE pattern is to distribute state-specific logic across classes that represent an object’s state.状态模式会将与状态有关的逻辑分布写在代表对象状态的类中
这句话英文读起来还是挺好理解的,中文相对更绕一些。简而言之,machine是一种抽象的概念,代表某一个流程或者是原理,并不是我们理解的机器。所以状态机也不是一个机器,它是由多个代表状态的类组合而成的流程或者说模式。也就是说我们会把DAG当中的每一个节点(状态)单独实现成一个类,那么整个DAG就是一系列状态类构成的图。
对于每个状态类而言,它们的操作应该都是类似的,就是初始化、执行以及转移。在一些系统当中,甚至可以没有执行只有转移。既然所有状态的操作都是类似的,那么我们可以对所有的状态抽象出统一的接口。这里我们多了一个is_end方法,代表某一个状态是否是整个流程的结束,如果是的话,我们就不需要继续转移了,直接退出即可。
代码语言:javascript复制class State:
def __init__(states):
pass
def determine(param):
pass
def operate():
pass
def is_end():
pass
同样,我们可以实现状态机的类。
代码语言:javascript复制class StateMachine:
def __init__():
self.node = StartState()
def init():
self.node = StartState()
def run(param):
while not self.node.is_end():
self.node = self.node.determine(param)
self.node.operate()
由于状态之间转移以及执行的逻辑都被封装在不同的类当中了,所以对于状态机而言,里面的逻辑非常简单,一般也不需要太大的修改。即使整个流程或者是某一个状态的条件发生变动, 我们也只需要修改对应节点的代码即可,并不会影响整体,非常适合用在那些流程经常发生变动的场景。
最后,我们来看一个状态机的使用案例。这个案例源于github,是一个将状态机应用在收音机上的case,具体的细节查看代码即可。
代码语言:javascript复制class State:
def scan(self):
# 模拟收音机的仪表盘,只能一个方向转动
self.pos = 1
if self.pos == len(self.stations):
self.pos = 0
print('Scanning... Station is {} {}'.format(self.stations[self.pos], self.name))
class AmState(State):
# Am 音频的类
def __init__(self, radio):
self.radio = radio
self.stations = ['1250', '1380', '1510']
self.pos = 0
self.name = 'AM'
def toggle_amfm(self):
# 转移到Fm
print('Switching to FM')
self.radio.state = self.radio.fmstate
class FmState(State):
# Fm 音频类
def __init__(self, radio):
self.radio = radio
self.stations = ['81.3', '89.1', '103.9']
self.pos = 0
self.name = 'FM'
def toggle_amfm(self):
# 转移到Am
print('Switching to AM')
self.radio.state = self.radio.amstate
class Radio:
# 收音机的整体类,也就是状态机类
def __init__(self):
self.amstate = AmState(self)
self.fmstate = FmState(self)
self.state = self.amstate
def toggle_amfm(self):
self.state.toggle_amfm()
def scan(self):
self.state.scan()
if __name__ == '__main__':
radio = Radio()
actions = [radio.scan] * 2 [radio.toggle_amfm] [radio.scan] * 2
actions *= 2
for action in actions:
action()
整个状态机的设计模式本身并不复杂,更多的是对这个设计理念和思想的理解,代码和形式都是表象。