golang有限状态机实现与管理插件库fsm的使用

Golang有限状态机实现与管理插件库fsm的使用

简介

fsm是一个Golang包,允许你在Go代码中添加有限状态机(FSM)。它比github.com/looplab/fsm性能更高,内存分配更少。

基本用法

定义状态和事件

首先定义状态和事件作为int常量:

const (
    StateFoo fsm.State = iota
    StateBar
)

const (
    EventFoo fsm.Event = iota
    EventBar
)

创建状态机和定义转换

f := fsm.New(StateFoo)  // 初始状态为StateFoo

// 定义从StateFoo到StateBar的转换,当EventFoo事件发生时
f.Transition(
    fsm.On(EventFoo), fsm.Src(StateFoo),
    fsm.Dst(StateBar),
)

自定义检查和操作

f.Transition(
    fsm.Src(StateFoo), fsm.Check(func() bool {
        // 检查某些条件
        return true // 返回true允许转换
    }),
    fsm.Call(func() {
        // 转换时执行的操作
    }),
)

多次触发事件转换

// 当EventFoo事件第二次发生时才会触发转换
f.Transition(
    fsm.On(EventFoo), fsm.Src(StateFoo), fsm.Times(2),
    fsm.Dst(StateBar),
)

状态进入和退出回调

// 进入特定状态时的回调
f.EnterState(StateFoo, func() {
    // 进入StateFoo时执行
})

// 进入任何状态时的回调
f.Enter(func(state fsm.State) {
    // 进入任何状态时执行
})

// 退出特定状态时的回调
f.ExitState(StateFoo, func() {
    // 退出StateFoo时执行
})

// 退出任何状态时的回调
f.Exit(func(state fsm.State) {
    // 退出任何状态时执行
})

完整示例

package main

import (
	"fmt"
	"github.com/cocoonspace/fsm"
)

// 定义状态
const (
	StateIdle fsm.State = iota
	StateWorking
	StateDone
)

// 定义事件
const (
	EventStart fsm.Event = iota
	EventFinish
	EventReset
)

func main() {
	// 创建状态机,初始状态为Idle
	f := fsm.New(StateIdle)

	// 定义状态转换
	f.Transition(
		fsm.On(EventStart), fsm.Src(StateIdle),
		fsm.Dst(StateWorking),
	)

	f.Transition(
		fsm.On(EventFinish), fsm.Src(StateWorking),
		fsm.Dst(StateDone),
	)

	f.Transition(
		fsm.On(EventReset), fsm.Src(StateDone),
		fsm.Dst(StateIdle),
	)

	// 添加状态进入回调
	f.EnterState(StateWorking, func() {
		fmt.Println("开始工作...")
	})

	f.EnterState(StateDone, func() {
		fmt.Println("工作完成!")
	})

	// 触发事件
	fmt.Println("当前状态:", f.Current())
	f.Event(EventStart)  // 从Idle -> Working
	fmt.Println("当前状态:", f.Current())
	f.Event(EventFinish) // 从Working -> Done
	fmt.Println("当前状态:", f.Current())
	f.Event(EventReset)  // 从Done -> Idle
	fmt.Println("当前状态:", f.Current())
}

性能

fsm包比github.com/looplab/fsm性能更高:

BenchmarkCocoonSpaceFSM-12    	29371851	        40.32 ns/op	       0 B/op	       0 allocs/op
BenchmarkLooplabFSM-12        	 2438946	       487.8 ns/op	     320 B/op	       4 allocs/op

(基准测试数据针对两次执行转换)

安装

go get github.com/cocoonspace/fsm

贡献指南

欢迎贡献,只要:

  • 包含单元测试和注释
  • 不使用外部包

许可证

MIT - 参见LICENSE文件


更多关于golang有限状态机实现与管理插件库fsm的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang有限状态机实现与管理插件库fsm的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang有限状态机实现与fsm库使用指南

有限状态机(FSM)是编程中常用的设计模式,用于管理对象的状态转换。下面我将介绍如何在Golang中实现FSM以及使用fsm插件库。

基本有限状态机实现

首先我们来看一个简单的FSM实现:

package main

import "fmt"

// 定义状态类型
type State string

// 定义状态常量
const (
    StateIdle    State = "idle"
    StateRunning State = "running"
    StatePaused  State = "paused"
)

// 定义事件类型
type Event string

// 定义事件常量
const (
    EventStart  Event = "start"
    EventPause  Event = "pause"
    EventResume Event = "resume"
    EventStop   Event = "stop"
)

// 有限状态机结构
type FSM struct {
    current State
    // 状态转换规则:map[当前状态]map[事件]目标状态
    transitions map[State]map[Event]State
}

// 创建新的FSM
func NewFSM(initial State) *FSM {
    fsm := &FSM{
        current: initial,
        transitions: make(map[State]map[Event]State),
    }
    
    // 设置状态转换规则
    fsm.transitions[StateIdle] = map[Event]State{
        EventStart: StateRunning,
    }
    
    fsm.transitions[StateRunning] = map[Event]State{
        EventPause: StatePaused,
        EventStop:  StateIdle,
    }
    
    fsm.transitions[StatePaused] = map[Event]State{
        EventResume: StateRunning,
        EventStop:   StateIdle,
    }
    
    return fsm
}

// 处理事件
func (f *FSM) HandleEvent(event Event) error {
    if transitions, ok := f.transitions[f.current]; ok {
        if nextState, ok := transitions[event]; ok {
            fmt.Printf("Transitioning from %s to %s\n", f.current, nextState)
            f.current = nextState
            return nil
        }
    }
    return fmt.Errorf("invalid transition from %s with event %s", f.current, event)
}

func main() {
    fsm := NewFSM(StateIdle)
    
    // 测试状态转换
    events := []Event{EventStart, EventPause, EventResume, EventStop}
    for _, event := range events {
        if err := fsm.HandleEvent(event); err != nil {
            fmt.Println("Error:", err)
        }
    }
}

使用fsm库

Golang有一个流行的FSM库github.com/looplab/fsm,下面是使用示例:

package main

import (
	"fmt"
	"github.com/looplab/fsm"
)

func main() {
	// 定义FSM
	fsm := fsm.NewFSM(
		"idle", // 初始状态
		fsm.Events{
			// 格式: 事件名称, 源状态列表, 目标状态
			{Name: "start", Src: []string{"idle"}, Dst: "running"},
			{Name: "pause", Src: []string{"running"}, Dst: "paused"},
			{Name: "resume", Src: []string{"paused"}, Dst: "running"},
			{Name: "stop", Src: []string{"running", "paused"}, Dst: "idle"},
		},
		fsm.Callbacks{
			// 状态转换前后的回调函数
			"enter_state": func(e *fsm.Event) {
				fmt.Printf("Entering state: %s\n", e.Dst)
			},
			"leave_state": func(e *fsm.Event) {
				fmt.Printf("Leaving state: %s\n", e.Src)
			},
			"after_start": func(e *fsm.Event) {
				fmt.Println("After start event")
			},
		},
	)

	// 打印当前状态
	fmt.Println("Current state:", fsm.Current())

	// 触发事件
	err := fsm.Event("start")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Current state:", fsm.Current())

	err = fsm.Event("pause")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Current state:", fsm.Current())

	err = fsm.Event("resume")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Current state:", fsm.Current())

	err = fsm.Event("stop")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Current state:", fsm.Current())
}

fsm库高级特性

  1. 条件转换:可以在回调中添加条件逻辑
fsm := fsm.NewFSM(
    "closed",
    fsm.Events{
        {Name: "open", Src: []string{"closed"}, Dst: "open"},
        {Name: "close", Src: []string{"open"}, Dst: "closed"},
    },
    fsm.Callbacks{
        "before_open": func(e *fsm.Event) {
            if someCondition {
                e.Cancel()
            }
        },
    },
)
  1. 异步事件处理
fsm.AsyncEvent("open", func(e *fsm.Event) {
    if e.Err != nil {
        fmt.Println("Error:", e.Err)
    } else {
        fmt.Println("Door opened successfully")
    }
})
  1. 获取所有可能的事件
availableEvents := fsm.AvailableTransitions()
fmt.Println("Available events:", availableEvents)

实际应用示例:订单状态机

package main

import (
	"fmt"
	"github.com/looplab/fsm"
)

type Order struct {
	ID    string
	State string
	FSM   *fsm.FSM
}

func NewOrder(id string) *Order {
	order := &Order{
		ID:    id,
		State: "created",
	}

	order.FSM = fsm.NewFSM(
		"created",
		fsm.Events{
			{Name: "pay", Src: []string{"created"}, Dst: "paid"},
			{Name: "ship", Src: []string{"paid"}, Dst: "shipped"},
			{Name: "deliver", Src: []string{"shipped"}, Dst: "delivered"},
			{Name: "cancel", Src: []string{"created", "paid"}, Dst: "cancelled"},
		},
		fsm.Callbacks{
			"enter_state": func(e *fsm.Event) {
				order.State = e.Dst
				fmt.Printf("Order %s changed state to %s\n", order.ID, order.State)
			},
		},
	)

	return order
}

func main() {
	order := NewOrder("12345")

	// 处理订单流程
	events := []string{"pay", "ship", "deliver"}
	for _, event := range events {
		err := order.FSM.Event(event)
		if err != nil {
			fmt.Printf("Failed to process %s: %v\n", event, err)
			break
		}
	}

	// 尝试非法转换
	err := order.FSM.Event("pay")
	if err != nil {
		fmt.Println("Expected error:", err)
	}
}

总结

Golang中实现有限状态机有多种方式:

  1. 自定义实现 - 适合简单场景
  2. 使用fsm库 - 提供更丰富的功能,如回调、异步事件等

fsm库的主要优点:

  • 清晰的状态转换定义
  • 丰富的回调机制
  • 线程安全
  • 支持条件转换
  • 提供当前可用事件查询

在实际项目中,FSM特别适合管理有明确状态转换的业务对象,如订单、工作流、游戏角色状态等。

回到顶部