FSM状态机的应用场景
FSM(有限状态机)广泛应用于各种系统中,尤其适用于那些具有有限状态且状态之间存在明确转换规则的场景。以下是一些典型的应用场景:
- 游戏开发:处理游戏中的不同状态(如菜单、游戏进行中、暂停、游戏结束等)。
- 用户认证系统:管理用户的登录、注销、认证等状态。
- 网络协议解析:处理协议的不同阶段(如握手、数据传输、断开连接等)。
- 设备驱动程序:管理硬件设备的不同操作模式。
- 工作流管理:处理复杂业务流程中的各个步骤和状态。
主要方法的使用
- NewFSM:创建并初始化一个新的FSM实例。
- Event:触发一个事件,进行状态转换并调用相关回调函数。
- Current:返回FSM当前的状态。
例子及其解释
我们将创建一个简单的游戏状态机,包含以下状态和事件:
- 状态:
-
- StateIdle:空闲状态
- StatePlaying:游戏进行状态
- StatePaused:暂停状态
- StateGameOver:游戏结束状态
- 事件:
-
- EventStart:开始游戏
- EventPause:暂停游戏
- EventResume:恢复游戏
- EventEnd:结束游戏
状态机实现
状态机文件 fsm.go
package api
import (
"time"
)
// Event 是在回调中传递的事件信息。
type Event struct {
FSM *FSM // FSM 是当前状态机的引用。
Event int // Event 是事件的名称。
Src int // Src 是状态转换前的状态。
Dst int // Dst 是状态转换后的状态。
Err error // Err 是可选的错误信息,可以在回调中返回。
Args []interface{} // Args 是传递给回调函数的可选参数列表。
}
// FSM 表示有限状态机。
type FSM struct {
current int // current 表示状态机的当前状态。
transitions map[eKey]int // transitions 映射事件和源状态到目标状态。
callbacks map[cKey]Callback // callbacks 映射事件和目标到回调函数。
StateChangeTime time.Time // StateChangeTime 记录状态改变的时间。
}
// EventDesc 定义了状态机的事件描述。
type EventDesc struct {
Name int // Name 是事件名称,用于触发状态转换。
Src []int // Src 是源状态的切片,定义了哪些状态可以触发该事件。
Dst int // Dst 是事件触发后的目标状态。
Before Callback // Before 是在事件触发前调用的回调函数。
Enter Callback // Enter 是在进入目标状态时调用的回调函数。
After Callback // After 是在事件触发后调用的回调函数。
}
// Callback 是回调函数的类型定义。
type Callback func(*Event)
// cKey 是用于将回调映射到目标的键结构。
type cKey struct {
target int // target 是目标状态。
callbackType int // callbackType 是回调类型(如前置、进入、后置回调)。
}
// eKey 是用于存储转换映射的键结构。
type eKey struct {
event int // event 是事件的名称。
src int // src 是源状态。
}
// NewFSM 创建并初始化一个新的 FSM 实例。
func NewFSM(initial int, events []EventDesc) *FSM {
f := &FSM{
current: initial, // 初始化状态机的当前状态。
transitions: make(map[eKey]int), // 初始化状态转换映射。
callbacks: make(map[cKey]Callback), // 初始化回调函数映射。
StateChangeTime: time.Now(), // 设置初始状态改变时间。
}
// 设置事件描述中的状态转换和回调函数。
for _, e := range events {
for _, src := range e.Src {
f.transitions[eKey{e.Name, src}] = e.Dst
}
if e.Before != nil {
f.callbacks[cKey{e.Name, callbackBeforeEvent}] = e.Before
}
if e.Enter != nil {
f.callbacks[cKey{e.Name, callbackEnterState}] = e.Enter
}
if e.After != nil {
f.callbacks[cKey{e.Name, callbackAfterEvent}] = e.After
}
}
return f
}
// Event 触发一个事件,进行状态转换并调用相关回调函数。
func (f *FSM) Event(event int, args ...interface{}) bool {
if f == nil {
return false // 状态机实例为空时,返回false。
}
dst, ok := f.transitions[eKey{event, f.current}]
if !ok {
fmt.Println("未找到状态转换") // 如果找不到状态转换,打印错误信息。
return false
}
e := &Event{f, event, f.current, dst, nil, args} // 创建事件对象。
f.beforeEventCallbacks(e) // 调用事件触发前的回调函数。
f.current = dst // 更新状态机的当前状态。
f.StateChangeTime = time.Now() // 更新状态改变时间。
f.enterStateCallbacks(e) // 调用进入状态的回调函数。
f.afterEventCallbacks(e) // 调用事件触发后的回调函数。
return true
}
// beforeEventCallbacks 调用事件触发前的回调函数。
func (f *FSM) beforeEventCallbacks(e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackBeforeEvent}]; ok {
fn(e)
}
}
// enterStateCallbacks 调用进入状态的回调函数。
func (f *FSM) enterStateCallbacks(e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackEnterState}]; ok {
fn(e)
}
}
// afterEventCallbacks 调用事件触发后的回调函数。
func (f *FSM) afterEventCallbacks(e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackAfterEvent}]; ok {
fn(e)
}
}
// Current 返回 FSM 当前的状态。
func (f *FSM) Current() int {
return f.current // 返回当前状态。
}
// 回调类型常量
const (
callbackNone int = iota
callbackBeforeEvent
callbackEnterState
callbackAfterEvent
)
主文件 main.go
package main
import (
"fmt"
"api" // 假设 fsm.go 文件在 api 包中
)
// 状态常量
const (
StateIdle = iota // 空闲状态
StatePlaying // 游戏进行状态
StatePaused // 暂停状态
StateGameOver // 游戏结束状态
)
// 事件常量
const (
EventStart = iota // 开始游戏事件
EventPause // 暂停游戏事件
EventResume // 恢复游戏事件
EventEnd // 结束游戏事件
)
// 回调函数示例
func beforeStart(e *api.Event) {
fmt.Println("Before starting the game.")
}
func onEnterPlaying(e *api.Event) {
fmt.Println("Entering playing state.")
}
func afterEnd(e *api.Event) {
fmt.Println("After ending the game.")
}
func main() {
// 定义事件描述
events := []api.EventDesc{
{Name: EventStart, Src: []int{StateIdle}, Dst: StatePlaying, Before: beforeStart, Enter: onEnterPlaying},
{Name: EventPause, Src: []int{StatePlaying}, Dst: StatePaused},
{Name: EventResume, Src: []int{StatePaused}, Dst: StatePlaying},
{Name: EventEnd, Src: []int{StatePlaying, StatePaused}, Dst: StateGameOver, After: afterEnd},
}
// 创建 FSM 实例
fsm := api.NewFSM(StateIdle, events)
// 触发事件
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 0 (StateIdle)
fsm.Event(EventStart)
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 1 (StatePlaying)
fsm.Event(EventPause)
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 2 (StatePaused)
fsm.Event(EventResume)
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 1 (StatePlaying)
fsm.Event(EventEnd)
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 3 (StateGameOver)
}
例子的详细解释
- 定义状态和事件常量:首先,我们定义了状态和事件的常量,以便在代码中使用。
- 回调函数:然后,我们定义了一些回调函数,这些函数将在状态转换前后触发。
- 事件描述:接下来,我们定义了事件描述,每个事件描述包含事件名称、源状态、目标状态和可选的回调函数。
- 创建FSM实例:使用定义的事件描述,我们创建了一个FSM实例,初始状态为
StateIdle。 - 触发事件并检查状态:最后,我们触发了一系列事件,并在每次事件后检查当前状态,输出结果如下:
-
- 开始时状态为
StateIdle。 - 触发
EventStart后状态变为StatePlaying。 - 触发
EventPause后状态变为StatePaused。 - 触发
EventResume后状态变为StatePlaying。 - 触发
EventEnd后状态变为StateGameOver。
- 开始时状态为
通过这个例子,可以清晰地看到FSM如何管理状态转换以及在转换过程中如何调用回调函数。这种设计使得状态管理变得更加明确和易于维护。
实操代码
fsm.go
package main
import (
"fmt"
"time"
)
// Event 是在回调中传递的事件信息。
type Event struct {
FSM *FSM
Event int
Src int
Dst int
Err error
Args []interface{}
}
// FSM 表示有限状态机。
type FSM struct {
current int
transitions map[eKey]int
callbacks map[cKey]Callback
StateChangeTime time.Time
}
// 事件描述
type EventDesc struct {
Name int
Src []int
Dst int
Before Callback
Enter Callback
After Callback
}
// 回调函数类型
type Callback func(*Event)
type cKey struct {
target int
callbackType int
}
type eKey struct {
event int
src int
}
// NewFSM 创建并初始化一个新的 FSM 实例。
func NewFSM(initial int, events []EventDesc) *FSM {
f := &FSM{
current: initial,
transitions: make(map[eKey]int),
callbacks: make(map[cKey]Callback),
StateChangeTime: time.Now(),
}
for _, e := range events {
for _, src := range e.Src {
f.transitions[eKey{e.Name, src}] = e.Dst
}
if e.Before != nil {
f.callbacks[cKey{e.Name, callbackBeforeEvent}] = e.Before
}
if e.Enter != nil {
f.callbacks[cKey{e.Name, callbackEnterState}] = e.Enter
}
if e.After != nil {
f.callbacks[cKey{e.Name, callbackAfterEvent}] = e.After
}
}
return f
}
// Event 触发一个事件,进行状态转换并调用相关回调函数。
func (f *FSM) Event(event int, args ...interface{}) bool {
if f == nil {
return false
}
dst, ok := f.transitions[eKey{event, f.current}]
if !ok {
fmt.Println("未找到状态转换")
return false
}
e := &Event{f, event, f.current, dst, nil, args}
f.beforeEventCallbacks(e)
f.current = dst
f.StateChangeTime = time.Now() // 更新状态改变时间
f.enterStateCallbacks(e)
f.afterEventCallbacks(e)
return true
}
func (f *FSM) beforeEventCallbacks(e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackBeforeEvent}]; ok {
fn(e)
}
}
func (f *FSM) enterStateCallbacks(e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackEnterState}]; ok {
fn(e)
}
}
func (f *FSM) afterEventCallbacks(e *Event) {
if fn, ok := f.callbacks[cKey{e.Event, callbackAfterEvent}]; ok {
fn(e)
}
}
// Current 返回 FSM 当前的状态。
func (f *FSM) Current() int {
return f.current
}
const (
callbackNone int = iota
callbackBeforeEvent
callbackEnterState
callbackAfterEvent
)
main.go
package main
import (
"fmt"
)
// 状态常量
const (
StateIdle = iota
StatePlaying
StatePaused
StateGameOver
)
// 事件常量
const (
EventStart = iota
EventPause
EventResume
EventEnd
)
// 回调函数示例
func beforeStart(e *Event) {
fmt.Println("Before starting the game.")
}
func onEnterPlaying(e *Event) {
fmt.Println("Entering playing state.")
}
func afterEnd(e *Event) {
fmt.Println("After ending the game.")
}
func main() {
// 定义事件描述
events := []EventDesc{
{Name: EventStart, Src: []int{StateIdle}, Dst: StatePlaying, Before: beforeStart, Enter: onEnterPlaying},
{Name: EventPause, Src: []int{StatePlaying}, Dst: StatePaused},
{Name: EventResume, Src: []int{StatePaused}, Dst: StatePlaying},
{Name: EventEnd, Src: []int{StatePlaying, StatePaused}, Dst: StateGameOver, After: afterEnd},
}
// 创建 FSM 实例
fsm := NewFSM(StateIdle, events)
// 触发事件
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 0 (StateIdle)
fsm.Event(EventStart) // Before starting the game. Entering playing state.
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 1 (StatePlaying)
fsm.Event(EventPause)
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 2 (StatePaused)
fsm.Event(EventResume)
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 1 (StatePlaying)
fsm.Event(EventEnd) // After ending the game.
fmt.Println("Current state:", fsm.Current()) // 输出: Current state: 3 (StateGameOver)
}