有限状态机

好处:逻辑收敛

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)
    }
}
相关推荐
IT_陈寒23 分钟前
Java序列化踩坑记:对象循环引用让我加班两小时
前端·人工智能·后端
泡海椒32 分钟前
05-雷达图多维画像-信用评级多维画像
后端
王中阳Go33 分钟前
5.27 分钟干完后端和全部测试:一场三小时笔试的五个失分点,四个不在代码上
大数据·数据库·人工智能·后端·大语言模型
大哥430934 分钟前
Modbus 的"字序":一个配错就变成天文数字,或者干脆变成 0
后端
程序猿老A36 分钟前
轻量云服务器总是卡,内存占比高?
运维·服务器
岩钧38 分钟前
New API 源码解析:一个生产级 AI 网关的架构与实现
后端
学到老活到老44 分钟前
第 24 集:配置文件写了 80,Spring Boot 为什么读到 90?
java·后端·spring
用户360055579001 小时前
Kestrel_VLLM - 25 · 全注意力层设备化、整层对拍与访存粒度
后端
yliucherish1 小时前
从零实现一个 SQL 注入检测引擎:报错注入与布尔盲注的误报控制
后端
bigbread1 小时前
状态模式-住院患者状态管理
后端