有限状态机

好处:逻辑收敛

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)
    }
}
相关推荐
凌云拓界1 小时前
TypeWell全攻略:AI健康教练+实时热力图开发实战 引言
前端·人工智能·后端·python·交互·pyqt·数据可视化
一路往蓝-Anbo2 小时前
第 10 章:OpenAMP 实战——构建 M33 与 Linux 的 RPMsg 消息隧道
linux·运维·服务器·驱动开发·stm32·单片机·嵌入式硬件
UrbanJazzerati2 小时前
Python Logging库完全指南:从小白到熟练
后端·面试
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 基于SpringBoot Vue的网络课程销售管理系统为例,包含答辩的问题和答案
java·spring boot·后端
Gopher_HBo2 小时前
Go进阶之示例测试原理
后端
钟智强2 小时前
深度剖析CVE-2023-41064与CVE-2023-4863:libwebp堆溢出漏洞的技术解剖与PoC构建实录
前端·后端
玄〤2 小时前
个人博客网站搭建day3--Spring Boot JWT Token 认证配置的完整实现详解(漫画解析)
java·spring boot·后端·jwt
钟智强2 小时前
MySQL客户端惊现高危漏洞CVE-2023-21980,可导致远程代码执行
前端·后端
用户3521802454752 小时前
RAG 做不好?可能是你的 PDF 在"捣乱" 😅
后端·python·ai编程