有限状态机

好处:逻辑收敛

1.状态转换更加清晰

2.新增转化逻辑,其他地方的代码不用修改

比如对于这样一个状态机:

|----|------------------------|
| 状态 | [草稿,审阅中,已拒绝,已通过,已归档] |
| 事件 | [提交,撤回,拒绝......] |

Go 复制代码
If else写法:
func docIfElse(events []string) {
    state := "草稿"
    
    for _, e := range events {
        fmt.Printf("  [%s] + <%s> → ", state, e)

        if state == "草稿" && e == "提交" {
            state = "审阅中"
        } else if state == "审阅中" && e == "通过" {
            state = "已通过"
        } else if state == "审阅中" && e == "拒绝" {
            state = "已拒绝"
        } else if state == "审阅中" && e == "撤回" {
            state = "草稿"
        } else if state == "已拒绝" && e == "提交" {
            state = "审阅中"
        }
        // ★ 新增状态?要在这里继续堆 else if...

        fmt.Printf("%s\n", state)
    }
}

状态机写法:

Go 复制代码
状态机写法:
// ----- 基础类型 -----
type Transition struct {
    Event string // 触发事件
    Next  string // 目标状态
}

type State struct {
    Name        string
    IsEnd       bool          // 终态标记
    Transitions []*Transition // 该状态的所有合法出口
}

type StateFlow []State

// ----- 引擎(永远不用改)-----

type FSM struct {
    current string
    flow    map[string]*State
}

func NewFSM(flow StateFlow, initial string) *FSM {
    m := make(map[string]*State, len(flow))
    for i := range flow {
        s := &flow[i]
        m[s.Name] = s
    }
    return &FSM{current: initial, flow: m}
}

func (f *FSM) Handle(event string) {
    st, ok := f.flow[f.current]
    if !ok {
        fmt.Printf("  ❓ 未知状态: %s\n", f.current)
        return
    }
    if st.IsEnd {
        fmt.Printf("  [%s] 是终态,不接受任何事件\n", f.current)
        return
    }
    for _, t := range st.Transitions {
        if t.Event == event {
            fmt.Printf("  [%s] + <%s> → %s\n", f.current, event, t.Next)
            f.current = t.Next
            return
        }
    }
    fmt.Printf("  [%s] + <%s> → 无效事件,保持原状态\n", f.current, event)
}

// ============================================================
//  定义文档审批流
// ============================================================

// 第一步:定义可复用的转换(定义一次,到处引用)
var (
    submitTransition   = &Transition{Event: "提交", Next: "审阅中"}
    approveTransition  = &Transition{Event: "通过", Next: "已通过"}
    rejectTransition   = &Transition{Event: "拒绝", Next: "已拒绝"}
    withdrawTransition = &Transition{Event: "撤回", Next: "草稿"}
    archiveTransition  = &Transition{Event: "归档", Next: "已归档"}
)

// 第二步:以"状态"为中心,声明每个状态的合法出口
var docFlow = StateFlow{
    {
        Name: "草稿",
        Transitions: []*Transition{
            submitTransition,   // → 审阅中
        },
    },
    {
        Name: "审阅中", // 表示审阅这个状态,可能会流转到已通过,已拒绝,草稿三个状态
        Transitions: []*Transition{
            approveTransition,  // → 已通过
            rejectTransition,   // → 已拒绝
            withdrawTransition, // → 草稿
        },
    },
    {
        Name: "已拒绝",
        Transitions: []*Transition{
            submitTransition,   // → 审阅中(复用同一个对象!)
        },
    },
    {
        Name: "已通过",
        Transitions: []*Transition{
            archiveTransition,  // → 已归档
        },
    },
    {
        Name:  "已归档",
        IsEnd: true,            // 终态
    },
}

func main() {
    fsm := NewFSM(docFlow, "草稿")
    events := []string{"提交", "拒绝", "提交", "通过", "归档", "提交"}
    for i, e := range events {
        fmt.Printf("[步骤 %d] ", i+1)
        fsm.Handle(e)
    }
}
相关推荐
oldking呐呐30 分钟前
MySQL从建库到删库跑路 -- 4.表的操作
后端·mysql
渐儿39 分钟前
I/O 多路复用与 Reactor 模式:高性能服务的根基
后端
空中海1 小时前
Spring Boot 专家级面试题库
spring boot·后端·面试
桌面运维家1 小时前
服务器进程异常监控:快速定位与排障实战指南
运维·服务器
fliter1 小时前
Cloudflare: Agent 现在可以自己注册账号、购买域名、部署上线了
后端
@CLoudbays_Martin111 小时前
UniApp是否能够接入SDK游戏盾呢?
服务器·网络·网络协议·tcp/ip·安全
郝亚军2 小时前
ubuntu 22.04如何安装libmodbus
运维·服务器·ubuntu
李日灐2 小时前
< 6 > Linux 自动化构建工具:makefile 详解 + 进度条实战小项目
linux·运维·服务器·后端·自动化·进度条·makefile
蝎子莱莱爱打怪2 小时前
小孩儿才做选择!Hermes 和OpenClaw 我都要!
人工智能·后端·github
直奔標竿2 小时前
SpringAI + RAG + MCP + Agent 零基础全栈实战(完结篇)| 27课完整汇总,Java开发者AI转型必看
java·开发语言·人工智能·spring boot·后端·spring