简介
在高质量编码的理念中,需要把控制逻辑和业务逻辑进行区分,这样的实现代码才更清晰,也更好维护;fsm有限状态机是其中一个很好的案例,这里介绍下go语言中的使用示例,详情可参考github.com/looplab/fsm
示例
源码如下:
package main
import (
"context"
"fmt"
"github.com/looplab/fsm"
)
// Door 定义一个结构体类型
type Door struct {
Name string
FSM *fsm.FSM
}
// NewDoor 初始化结构体以及状态机
func NewDoor(name string) *Door {
d := &Door{
Name: name,
}
d.FSM = fsm.NewFSM(
// 初始状态
"closed",
// 有限状态机
fsm.Events{
// 事件名称、源状态,目的状态
{Name: "OPEN", Src: []string{"closed"}, Dst: "opened"},
{Name: "CLOSE", Src: []string{"opened"}, Dst: "closed"},
},
// 执行函数(动作)
// 支持具体的状态和事件before_<EVENT>、leave_<OLD_STATE>、enter_<NEW_STATE>、after_<EVENT>
fsm.Callbacks{
"before_event": func(_ context.Context, e *fsm.Event) { d.beforeEvent(e) },
"after_event": func(_ context.Context, e *fsm.Event) { d.afterEvent(e) },
"enter_state": func(_ context.Context, e *fsm.Event) { d.enterState(e) },
"leave_state": func(_ context.Context, e *fsm.Event) { d.leaveState(e) },
},
)
return d
}
// 定义before_event的回调函数,即事件触发前会调用回调函数
func (d *Door) beforeEvent(e *fsm.Event) {
fmt.Printf("[before_event]The door name[%s] is from %s to %s\n", d.Name, e.Src, e.Dst)
}
// 定义after_event的回调函数,即事件触发后会调用回调函数
func (d *Door) afterEvent(e *fsm.Event) {
fmt.Printf("[after_event]The door name[%s] is from %s to %s\n", d.Name, e.Src, e.Dst)
}
// 定义enter_state的回调函数,即进入一个状态时会调用回调函数
func (d *Door) enterState(e *fsm.Event) {
fmt.Printf("[enter_state]The door name[%s] is from %s to %s\n", d.Name, e.Src, e.Dst)
}
// 定义leave_state的回调函数,即离开一个状态时会调用回调函数
func (d *Door) leaveState(e *fsm.Event) {
fmt.Printf("[leave_state]The door name[%s] is from %s to %s\n", d.Name, e.Src, e.Dst)
}
func main() {
door := NewDoor("mydoor")
fmt.Println("==>OPEN the door!")
// 触发OPEN事件
err := door.FSM.Event(context.Background(), "OPEN")
if err != nil {
fmt.Println(err)
}
fmt.Println("==>OPEN the door again!")
// 触发OPEN事件
err = door.FSM.Event(context.Background(), "OPEN")
if err != nil {
fmt.Println(err)
}
fmt.Println("==>CLOSE the door!")
// 触发CLOSE事件
err = door.FSM.Event(context.Background(), "CLOSE")
if err != nil {
fmt.Println(err)
}
}
运行结果如下:
[xiaofeng@golang-dev gofsm]$ go run main.go
==>OPEN the door!
[before_event]The door name[mydoor] is from closed to opened
[leave_state]The door name[mydoor] is from closed to opened
[enter_state]The door name[mydoor] is from closed to opened
[after_event]The door name[mydoor] is from closed to opened
==>OPEN the door again!
event OPEN inappropriate in current state opened
==>CLOSE the door!
[before_event]The door name[mydoor] is from opened to closed
[leave_state]The door name[mydoor] is from opened to closed
[enter_state]The door name[mydoor] is from opened to closed
[after_event]The door name[mydoor] is from opened to closed
[xiaofeng@golang-dev gofsm]$
总结
fsm有限状态机核心三要素:事件、状态、动作,可以按如下思路进行实现
- 枚举出所有可能的状态。
- 枚举出所有可能的事件。
- 事件和状态的对应关系,即触发一个事件后,状态会从什么变成什么。
- 补充动作,即在某些事件发生或状态变更的前后需要执行的动作。
- 优点是控制逻辑一开始定义好,业务逻辑都封装在执行函数,实现很清晰。