Golang实现有限状态机(FSM)的设计模式

在Golang中实现有限状态机(FSM)时,有哪些推荐的设计模式或最佳实践?目前想用FSM管理订单状态流转(如待支付、已支付、已取消等),但不确定如何优雅地处理状态转换逻辑和业务代码解耦。是用switch-case硬编码状态转换,还是用状态模式+接口封装更合适?有没有开源库或典型实现可以参考?

2 回复

在Golang中实现有限状态机(FSM),通常使用结构体封装状态和转换逻辑。以下是一个简洁示例:

type FSM struct {
    current string
    states  map[string]State
}

type State func() string

// 添加状态
func (f *FSM) AddState(name string, state State) {
    f.states[name] = state
}

// 转换状态
func (f *FSM) Transition(name string) {
    if _, exists := f.states[name]; exists {
        f.current = name
    }
}

// 执行当前状态
func (f *FSM) Execute() {
    if state, exists := f.states[f.current]; exists {
        next := state()
        f.Transition(next)
    }
}

使用示例:

fsm := &FSM{states: make(map[string]State)}
fsm.AddState("start", func() string {
    fmt.Println("Starting...")
    return "end"
})
fsm.Transition("start")
fsm.Execute()

核心思路:

  1. 用map存储状态函数
  2. 每个状态返回下一个状态名
  3. Transition方法切换状态
  4. Execute执行当前状态并自动转换

这种实现简单直观,适合业务逻辑不复杂的场景。实际使用时可以加入状态转换校验、历史记录等扩展功能。

更多关于Golang实现有限状态机(FSM)的设计模式的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,实现有限状态机(FSM)通常采用状态模式,通过结构体和方法来封装状态转移逻辑。以下是一个简洁的实现示例:

核心设计

  1. 定义状态接口:所有状态实现同一接口,包含进入、退出和执行行为的方法。
  2. 上下文结构体:维护当前状态,并触发状态转移。
  3. 具体状态:实现接口,封装特定状态的逻辑。

代码示例

package main

import "fmt"

// 状态接口
type State interface {
    Enter()
    Exit()
    Handle(context *Context)
}

// 上下文,维护当前状态
type Context struct {
    currentState State
}

func (c *Context) SetState(state State) {
    if c.currentState != nil {
        c.currentState.Exit()
    }
    c.currentState = state
    c.currentState.Enter()
}

func (c *Context) Request() {
    c.currentState.Handle(c)
}

// 具体状态A
type StateA struct{}

func (s *StateA) Enter() { fmt.Println("进入状态A") }
func (s *StateA) Exit()  { fmt.Println("离开状态A") }
func (s *StateA) Handle(ctx *Context) {
    fmt.Println("状态A处理请求,转移到状态B")
    ctx.SetState(&StateB{})
}

// 具体状态B
type StateB struct{}

func (s *StateB) Enter() { fmt.Println("进入状态B") }
func (s *StateB) Exit()  { fmt.Println("离开状态B") }
func (s *StateB) Handle(ctx *Context) {
    fmt.Println("状态B处理请求,转移到状态A")
    ctx.SetState(&StateA{})
}

// 使用示例
func main() {
    context := &Context{}
    context.SetState(&StateA{}) // 初始状态
    
    context.Request() // 触发转移:A -> B
    context.Request() // 触发转移:B -> A
}

执行结果

进入状态A
状态A处理请求,转移到状态B
离开状态A
进入状态B
状态B处理请求,转移到状态A
离开状态B
进入状态A

关键点

  • 状态转移:通过Context.SetState()方法切换状态,自动调用旧状态的Exit()和新状态的Enter()
  • 扩展性:新增状态只需实现State接口,无需修改现有代码。
  • 适用场景:工作流、游戏角色行为、网络协议等有明确状态转移逻辑的系统。

此设计避免了复杂的条件判断,使状态逻辑清晰且易于维护。

回到顶部