STATE模式

STATE模式

  有限状态自动机,FSM,是一种抽象,为了将复杂问题简单化,容易理解;并且易于实现和修改。使用有限状态自动机的好处是,即使是复杂系统,也可以清晰的展现系统状态的变化,并且对于各种状态的变化不容易遗漏。

1. 问题描述

    分布式系统中,守护进程通常用来监控节点的资源和进程,管理其生命周期,并上报到集群控制模块,例如kuberntes中的kubelet模块。
在这里使用项目中实际遇到的情况通过简化之后作为用例。
    集群的每个节点中都拥有多数量多类型执行器。执行器拥有四个状态为: 初始化(initialize)、空闲(free)、运行中(running)和不健康状态(unhealthy)。守护进程根据执行器的状态变化,执行相应的操作,如:

  • 执行器处于初始化的状态,守护进程收到状态变为空闲状态的请求时,将执行器状态变为空闲状态,并且将可用资源加一。
  • 执行器处于空闲状态,守护进程收到状态变为初始化状态的请求时,将执行器状态变为初始化状态,并且将可用资源减一。

    上面只是列举了两个状态的迁移。因为执行器一共拥有4个状态,在处于一个状态时,守护进程可能会收到四类不同状态的请求,并且执行相应的操作,根据笛卡尔乘积,所以一共有16个可能的组合。上述举例只为其中的两个情况。如何清晰的将状态的变化和要执行的操纵表达出来,并且做到不遗漏某种情况呢?因为主线是大多数都能想到的情况,很多程序异常变来自没有考虑到的情况。

2. 有限状态自动机概述

有限状态自动机用来描述系统的变化情况,可以总结为系统处于一种状态,当发生某个事件时,系统会迁移到另一个状态,并且执行相应的操作。
上一小节中举例的两种情况,用状态迁移图来描述状态机如下:
在这里插入图片描述
上述状态机描述的含义和上一小节的举例相同:

  • 若状态机在initialize状态收到free请求,则迁移到free状态并执行可用执行器加一动作。
  • 若状态机在free状态收到initialize请求,则迁移到initialize状态并执行可用执行器减一动作。

每句话都用四个元素来描述一个箭头:初始状态,触发迁移的事件,终止状态以及要执行的操作。
使用状态迁移图可以很清晰、优雅的表达状态机。下面使用状态机来表达第一小节的守护进程对执行器的状态迁移以及相应的操作。
在没有使用状态机的情况下,在系统初期大多数会考虑的情况为主线情况和正常情况,使用状态迁移图描述为如下:
在这里插入图片描述

使用状态机来抽象描述系统的好处在于,即使在系统初期,设计人员便可以很容易的检测到未知的以及没有处理的情况。对于上图中状态迁移图没有描述到的迁移加以补充,便可得到如下状态迁移图:
在这里插入图片描述

3. 实现方法

因为第一节中的例子拥有四个状态,为了展示实现的各种方法,此处将四个状态缩减为两个状态,分别为初始化状态和空闲状态,描述状态机的状态迁移图如下:

在这里插入图片描述

嵌套if else语句

在没有使用状态机的情况下,往往容易使用if else语句来判断。但是当状态较多时,如第一节中的用例,使用if else语句实现的代码难以阅读理解,并且很难维护,编写状态条件语句时容易出现错误。

嵌套switch/case语句

实现状态机的最直接办法为switch/case语句。这里为了将描述有限状态机的迁移和执行相应动作的代码依赖进行分离,一共包含两个类,HandlerController为状态迁移后要执行的动作,HandlerMonitor用来描述状态记得迁移。因为python中没有实现switch/case语句,所以使用if else来代替。代码如下:

"""
使用switch/case语句实现有限状态自动机, 自动机为csdn中state模式里面的第三小节用例:
 - 若状态机在initialize状态收到free请求,则迁移到free状态并执行可用执行器加一动作。
 - 若状态机在free状态收到initialize请求,则迁移到initialize状态并执行可用执行器减一动作。
因为python中没有实现switch/case语句,所以使用if else语句代替
"""
from abc import ABCMeta, abstractmethod


class HandlerController(metaclass=ABCMeta):
    @abstractmethod
    def add_avail_handler(self):
        pass

    @abstractmethod
    def remove_avail_handler(self):
        pass

    @abstractmethod
    def update_info(self):
        pass


class HandlerMonitor:
    INITIALIZE: int = 0
    FREE: int = 1

    INITIALIZE_REQUEST: int = 0
    FREE_REQUEST: int = 1

    state: int = INITIALIZE
    handler_controller: HandlerController

    def __int__(self, controller: HandlerController):
        HandlerMonitor.handler_controller = controller

    def event(self, event: int) -> None:
        if self.state == self.INITIALIZE:
            if event == self.INITIALIZE_REQUEST:
                self.handler_controller.update_info()
            elif event == self.FREE_REQUEST:
                self.state = self.FREE
                self.handler_controller.add_avail_handler()
        elif self.state == self.FREE:
            if event == self.INITIALIZE_REQUEST:
                self.state = self.INITIALIZE
                self.handler_controller.remove_avail_handler()
            elif event == self.FREE_REQUEST:
                self.handler_controller.update_info()

在嵌套if else语句中将代码块分为了四个区域,每个区域对应状态机的一个迁移。
优点:实现状态比较少的简单状态自动机时此实现方法比较好,代码也很清晰。
代价:当状态自动机比较复杂时,如第一小节的自动机,使用此方法实现是代码冗长的,并且难以维护,容易出现错误。

解释迁移表

实现自动计的另一个方法为解释迁移表。该方法事先存储自动机中的每一条迁移,然后在事件处理的模块,在多个迁移中选择符合发生事件的迁移。这里展示部分代码。
transitions列表元素为状态自动机的每一条迁移,使用Transition对象来储存。在构造函数中,将所有的迁移通过add_transition方法加入到列表中。Transition对象则用来解释每一条迁移的四个元素:当前的状态,发生的事件,新的状态以及要执行的动作。

class Action(metaclass=ABCMeta):
    @abstractmethod
    def execute(self):
        pass

class HandlerMonitor:
    INITIALIZE: int = 0
    FREE: int = 1

    INITIALIZE_REQUEST: int = 0
    FREE_REQUEST: int = 1

    state: int = INITIALIZE
    handler_controller: HandlerController
    transitions: List = []

    class Transition:
        current_state: int
        event: int
        new_state: int
        action: Action

        def __init__(self, current_state: int, event: int, new_event: int, action: Action):
            self.current_state = current_state
            self.event = event
            self.new_state = new_event
            self.action = action

    def __int__(self, controller: HandlerController):
        HandlerMonitor.handler_controller = controller
        self.add_transition(self.INITIALIZE, self.INITIALIZE_REQUEST, self.INITIALIZE, self.update_info())
        self.add_transition(self.INITIALIZE, self.FREE_REQUEST, self.FREE, self.add_handler())
        self.add_transition(self.FREE, self.INITIALIZE_REQUEST, self.INITIALIZE, self.remove_handler())
        self.add_transition(self.FREE, self.FREE, self.FREE, self.update_info())

    def add_transition(self, current_state: int, event: int, new_state: int, action: Action):
        self.transitions.append(self.Transition(current_state, event, new_state, action))

在事件处理模块中,只需要从存储好的迁移表中获取符合目前情况的迁移,设置状态执行相应的操作便可。

    def event(self, event: int) -> None:
        for i in range(len(self.transitions)):
            transition = self.transitions[i]
            if self.state == transition.current_state and event == transition.event:
                self.state = transition.new_state
                transition.action.execute()

完整代码为:https://github.com/baozhiming/designPatterns/blob/main/finite_state_machine/transitions.py
好处:易于阅读,易于理解。通过四个add transition方法便可得倒整个的迁移表;状态自动机的逻辑描述都集中在一起,和动作实现节藕;灵活性:也可以动态的增加迁移;可以设置多个迁移表,动态选择不同的迁移表;
代价:收到每个时间,都需要遍历整个迁移表,平均时间复杂度为O(n),没有switch/case语句高效。

STATE模式

如何能够向switch/case方法一样高效,又可以和解释迁移表方法一样灵活。state模式给了一种设计方法。
STATE模式希望能够将状态机的逻辑和要执行的动作分离,使用State接口来描述一个状态,State接口没有任何的变量,只有要发生的动作。State接口的派生类代表状态。Context上下文类中存储着指向State对象的引用,并且在Context类中实现具体的动作。实现State接口的派生类成员方法则通过调用Context对象来实现相应的动作。
在这里插入图片描述
使用STATE模式实现上述的执行器状态转换,程序设计如下图。
在这里插入图片描述
其中的HandlerState接口和继承类代码如下,每个实现接口的继承类都包含每种事件发生要执行操作对应的方法。在这些方法中,调用Handler上下文类来改变状态机目前的状态,并执行相应的动作。例如,HandlerInitializeState中的free_request方法被执行时,意味着状态机目前处于initialize初始化状态,并且发生了free请求事件,HandlerInitializeState会把状态机的状态设置为freeState,并且调用Handler的add_handler方法,添加执行器。

class HandlerState(metaclass=ABCMeta):
    @abstractmethod
    def initialize_request(self, h):
        pass

    @abstractmethod
    def free_request(self, h):
        pass


class HandlerInitializeState(HandlerState):
    def initialize_request(self, h):
        h.update_info()

    def free_request(self, h):
        h.set_free_state()
        h.add_handler()


class HandlerFreeState(HandlerState):
    def initialize_request(self, h):
        h.set_initialize_state()
        h.remove_handler()

    def free_request(self, h):
        h.update_info()

上下文类Handler如下,其中对HandlerState的引用使用静态属性,因为HandlerState不存在任何变量,也不需要存在多个实例,所以为了避免创建多个Handler对象时产生多个HandlerState对象,所以这里使用静态属性。state属性用来表示目前系统处于何状态。当发生请求时,便调用当前状态类中对应的方法,更改目前状态,并执行相应的动作。

class Handler:
    initialize_state = HandlerInitializeState()
    free_state = HandlerFreeState()

    state = initialize_state
    controller: HandlerController

    def __init__(self, controller: HandlerController):
        self.controller = controller

    def free_request(self):
        self.state.free_request(self)

    def initialize_request(self):
        self.state.initialize_request(self)

    def set_initialize_state(self):
        self.state = self.initialize_state

    def set_free_state(self):
        self.state = self.free_state

    def add_handler(self):
        self.controller.add_avail_handler()

    def remove_handler(self):
        self.controller.remove_avail_handler()

    def update_info(self):
        self.controller.update_info()

    def is_initialize_state(self):
        return self.state == self.initialize_state

    def is_free_state(self):
        return self.state == self.free_state

使用STATE模式的好处:
可以将状态机自动机的状态更改逻辑和动作完全的分开解耦。动作在Context类中实现,状态逻辑在State接口的派生类中实现。如果做相应的更改不会影响到另一个模块。
效率高,拥有和switch/case方法相同的效率
代价:
当状态比较多的时候,要派生多个类,相对来讲比较乏味。
如果不了解STATE模式相对来讲会比较难维护。

有限状态自动机使用场景

对于事件驱动的系统,随着事件的发生,系统的状态会发生相应的改变,此时可以使用状态自动机来实现。可以对整个系统的状态转换清晰的表达出来。例如第一小节的例子;在发送和接受网络数据的系统中可以使用状态机来描述系统目前处于数据发送或者接收的某一个阶段等。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值