golang创建状态机的流畅库插件stateless的使用
Golang创建状态机的流畅库插件stateless的使用
介绍
Stateless是一个用于在Go代码中直接创建状态机和轻量级基于状态机的工作流的库。它基于UML状态图理论,帮助组织设备、计算机程序或其他技术流程的工作方式,使实体或其子实体始终处于多个可能状态中的一个,并且在这些状态之间有明确定义的条件转换。
主要特性
- 支持任何可比较类型的状态和触发器(int, strings, boolean, structs等)
- 分层状态
- 状态的进入/退出事件
- 支持条件转换的守卫条件
- 自省功能
- 外部状态存储
- 参数化触发器
- 可重入状态
- 线程安全
- 导出为DOT图
完整示例
下面是一个电话呼叫状态机的完整示例:
package main
import (
"context"
"fmt"
"github.com/qmuntal/stateless"
)
// 定义状态
const (
stateOffHook = iota
stateRinging
stateConnected
stateOnHold
statePhoneDestroyed
)
// 定义触发器
const (
triggerCallDialed = iota
triggerCallConnected
triggerLeftMessage
triggerPlacedOnHold
triggerTakenOffHold
triggerPhoneHurledAgainstWall
)
func main() {
// 创建状态机,初始状态为挂机状态
phoneCall := stateless.NewStateMachine(stateOffHook)
// 配置状态转换
phoneCall.Configure(stateOffHook).
Permit(triggerCallDialed, stateRinging)
phoneCall.Configure(stateRinging).
OnEntryFrom(triggerCallDialed, func(_ context.Context, args ...any) error {
fmt.Println("拨号中... 呼叫号码:", args[0].(string))
return nil
}).
Permit(triggerCallConnected, stateConnected)
phoneCall.Configure(stateConnected).
OnEntry(func(_ context.Context, _ ...any) error {
fmt.Println("开始通话计时...")
return nil
}).
OnExit(func(_ context.Context, _ ...any) error {
fmt.Println("停止通话计时...")
return nil
}).
Permit(triggerLeftMessage, stateOffHook).
Permit(triggerPlacedOnHold, stateOnHold)
phoneCall.Configure(stateOnHold).
SubstateOf(stateConnected).
Permit(triggerTakenOffHold, stateConnected).
Permit(triggerPhoneHurledAgainstWall, statePhoneDestroyed)
// 触发状态转换
err := phoneCall.Fire(triggerCallDialed, "1234567890")
if err != nil {
fmt.Println("触发拨号失败:", err)
return
}
err = phoneCall.Fire(triggerCallConnected)
if err != nil {
fmt.Println("触发连接失败:", err)
return
}
err = phoneCall.Fire(triggerPlacedOnHold)
if err != nil {
fmt.Println("触发保持失败:", err)
return
}
fmt.Println("当前状态:", phoneCall.MustState())
}
分层状态示例
phoneCall.Configure(stateOnHold).
SubstateOf(stateConnected). // OnHold是Connected的子状态
Permit(triggerTakenOffHold, stateConnected).
Permit(triggerPhoneHurledAgainstWall, statePhoneDestroyed)
进入/退出事件
phoneCall.Configure(stateConnected).
OnEntry(func(_ context.Context, _ ...any) error {
startCallTimer()
return nil
}).
OnExit(func(_ context.Context, _ ...any) error {
stopCallTimer()
return nil
})
外部状态存储
machine := stateless.NewStateMachineWithExternalStorage(
func(_ context.Context) (stateless.State, error) {
return myState.Value, nil
},
func(_ context.Context, state stateless.State) error {
myState.Value = state
return nil
},
stateless.FiringQueued)
守卫条件
phoneCall.Configure(stateOffHook).
Permit(triggerCallDialed, stateRinging, func(_ context.Context, _ ...any) bool {
return IsValidNumber()
}).
Permit(triggerCallDialed, stateBeeping, func(_ context.Context, _ ...any) bool {
return !IsValidNumber()
})
参数化触发器
stateMachine.SetTriggerParameters(triggerCallDialed, reflect.TypeOf(""))
stateMachine.Configure(stateRinging).
OnEntryFrom(triggerCallDialed, func(_ context.Context, args ...any) error {
fmt.Println(args[0].(string))
return nil
})
stateMachine.Fire(triggerCallDialed, "qmuntal")
导出为DOT图
sm := stateMachine.Configure(stateOffHook).
Permit(triggerCallDialed, stateRinging, isValidNumber)
graph := sm.ToGraph()
生成的DOT图示例:
digraph {
OffHook -> Ringing [label="CallDialled [isValidNumber]"];
}
项目目标
Stateless旨在保持简洁和最小化,同时提供强大的状态机功能。它实现了关于状态转换的规则集,但在使用委托版本的构造函数时,它本身不维护任何内部状态。
更多关于golang创建状态机的流畅库插件stateless的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复