自动化状态管理:优化项目开发中的状态流转

有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。

状态机是由以下几个核心组成:

  • 状态(state):表示我们常见的a->b->c的状态序列。
  • 事件(event):触发从一个状态到另一个状态转换的条件。
  • 动作(action):在从一个状态转换到另一个状态时执行的具体操作。
  • 转换(transition):表示从一个状态到另一个状态的过程。

使用状态机的原因

随着项目的迭代,状态的数量可能会越来越多。以前可能还能通过使用ifswitch语句来维护状态,但随着状态的增加,这种方法变得难以应付。状态的嵌套层级越来越深,并且许多产品还涉及到状态的回退,导致状态转换的判断变得复杂且难以维护。

使用状态机,我们可以根据预先定义好的状态自动进行状态流转。通过将状态和行为进行抽象和模块化,我们可以更好地管理和维护状态。

项目实践

下面是一个简单的示例,展示了一个真实项目中的状态流转:

状态 操作
标注待领取 1 领取: 标注中
标注中 2 提交: 内检待领取
内检待领取 3 领取: 内检中
内检中 4 提交: 验收待领取 打回: 标注待领取
验收待领取 5 领取: 验收中
验收中 6 提交: 已完成 打回: 内检待领取
已完成 7

在这个例子中,我们先构建了一个状态和操作的映射表(Map),然后根据事件来判断要执行的操作。如果存在对应的事件和操作,就进行状态的转换。这样就完成了整个流程。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main

import (
	"fmt"
)

func main() {
	Run(Default, Submit)
	Run(MarkPick, Submit)
	Run(Mark, Submit)
	Run(MarkPick, Receive)
	Run(Check, CallBack)
}

func Run(state State, event Event) {
	if v, ok := transitions[state][event]; ok {
		s := ""
		if v1, ok := stateName[state]; ok {
			s = v1
		}
		s2 := ""
		if v2, ok := stateName[v]; ok {
			s2 = v2
		}
		fmt.Printf("当前状态: %s, 事件: %s, 后来状态: %s\n", s, event, s2)
		return
	}
	fmt.Println("当前状态无法流转")
}

type State uint8

const (
	Default = iota
	MarkPick
	Mark
	InternalPick
	Internal
	CheckPick
	Check
	Finish
)

var stateName = map[State]string{
	Default:      "默认",
	MarkPick:     "标注待领取",
	Mark:         "标注中",
	InternalPick: "内检待领取",
	Internal:     "内检中",
	CheckPick:    "验收待领取",
	Check:        "验收中",
	Finish:       "已完成",
}

type Event string

const (
	Receive  = "Receive"
	Submit   = "Submit"
	CallBack = "CallBack"
)

var transitions = map[State]map[Event]State{
	MarkPick: {
		Receive: Mark,
	},
	Mark: {
		Submit: InternalPick,
	},
	InternalPick: {
		Receive: Internal,
	},
	Internal: {
		Submit:   CheckPick,
		CallBack: MarkPick,
	},
	CheckPick: {
		Receive: Check,
	},
	Check: {
		CallBack: InternalPick,
		Submit:   Finish,
	},
}

在这个示例中,我们定义了一个State类型和一个Event类型,分别表示状态和事件。然后,我们使用transitions来表示状态之间的转换关系,其中transitions是一个嵌套的映射表,它的键是起始状态,值是一个映射表,该映射表的键是事件,值是目标状态。

我们还定义了一个Run函数,它接受当前状态和触发的事件作为参数。在Run函数中,我们首先检查是否存在从当前状态到目标状态的转换路径,如果存在,就执行相应的操作,并输出当前状态、触发的事件和目标状态。如果不存在转换路径,就输出"当前状态无法流转"。

通过调用Run函数,我们可以模拟状态机的状态流转过程。在示例中的main函数中,我们执行了几个状态流转的例子,然后输出了相应的结果。

这个示例只是一个简单的演示,实际项目中的状态流转可能会更加复杂。在实际应用中,你可以根据项目的需求和业务逻辑来定义和扩展状态机的状态、事件和转换规则。

参考资料