简化状态机 (Python)

Simplifying State Machines (Python)

我想知道为什么状态机完全使用中介“状态”class (between the Concrete States and the Context)。从下面的模型中,这可以用一个简单的变量来完成。

例如,这里有一个简化的“包含”式状态机,它满足相同的目标。与使用传统状态机模型相比,我使用这个模型失去了什么?

此外,附带问题,包含这些子 class 是因为它们是缩进的并且是父项 (Lift) class 的一部分。这种继承方式与取消缩进并将它们设置为这样的方式有什么区别:class FloorOne(Lift):

class Lift:
    def __init__(self):
        self.current_state = Lift.FloorOne(self)

    def show_input_error(self):
        print("Incorrect input")

    class FloorOne:
        def __init__(self, lift):
            self.lift = lift
            print("You are on Floor One")

        def button_press(self, num):
            self.transition(num)

        def transition(self, num):
            if num == 2:
                self.lift.current_state = Lift.FloorTwo(self.lift)
            elif num == 3:
                self.lift.current_state = Lift.FloorThree(self.lift)
            else:
                self.lift.show_input_error()

    class FloorTwo:
        def __init__(self, lift):
            self.lift = lift
            print("You are on Floor Two")

        def button_press(self, num):
                self.transition(num)

        def transition(self, num):
            if num == 1:
                self.lift.current_state = Lift.FloorOne(self.lift)
            elif num == 3:
                self.lift.current_state = Lift.FloorThree(self.lift)
            else:
                self.lift.show_input_error()

    class FloorThree:
        def __init__(self, lift):
            self.lift = lift
            print("You are on Floor Three")

        def button_press(self, num):
            self.transition(num)

        def transition(self, num):
            if num == 1:
                self.lift.current_state = Lift.FloorOne(self.lift)
            elif num == 2:
                self.lift.current_state = Lift.FloorTwo(self.lift)
            else:
                self.lift.show_input_error()

lift = Lift()
while True:
    goto = input("What floor would you like to go to?")
    lift.current_state.button_press(int(goto))

如果您将所有楼层 classes 定义为来自共同 class 状态的子 classes,您将获得:

  1. 从代码中可以看出,这是具体状态的公共接口。甚至你可以强制一个接口添加抽象方法,这些抽象方法必须在具体 classes 中被覆盖才能被实例化

  2. 您必须减少代码,因为您可以在状态 class 中定义一次对所有状态都相等的方法。例如 button_press 方法。

  3. 使代码更容易更改。

看这段代码:

class Lift:
    def __init__(self):
        self.current_state = Lift.FloorOne(self)

    def show_input_error(self):
        print("Incorrect input")
        
    class State:
        def __init__(self, lift):
            self.lift = lift
            print(f'You are on Floor {self.floor_name}')

        def button_press(self, num):
            self.transition(num)

        def transition(self, num):
            if num != self.floor_num and num in [1,2,3]:
                self.lift.current_state = [Lift.FloorOne, 
                                           Lift.FloorTwo, 
                                           Lift.FloorThree][num - 1](self.lift)
                
            else:
                self.lift.show_input_error()

    class FloorOne(State):
        floor_name = 'One'
        floor_num = 1
        
    class FloorTwo(State):
        floor_name = 'Two'
        floor_num = 2

    class FloorThree(State):
        floor_name = 'Three'
        floor_num = 3
        
lift = Lift()
while True:
    goto = input("What floor would you like to go to?")
    lift.current_state.button_press(int(goto))

现在添加楼层更容易了。

如果需要,您可以覆盖子class 中的任何方法以获得不同的行为:

class BrokenLift(State):
    def transition(self, num):
        print('broken lift!')