有限状态机

好处:逻辑收敛

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)
    }
}
相关推荐
ん贤11 小时前
Go channel 深入解析
开发语言·后端·golang
一袋米扛几楼9813 小时前
【密码学】CrypTool2 工具是什么?
服务器·网络·密码学
changhong198614 小时前
如何在 Spring Boot 中配置数据库?
数据库·spring boot·后端
林姜泽樾16 小时前
Linux入门第十二章,创建用户、用户组、主组附加组等相关知识详解
linux·运维·服务器·centos
月月玩代码16 小时前
Actuator,Spring Boot应用监控与管理端点!
java·spring boot·后端
南棱笑笑生17 小时前
20260310在瑞芯微原厂RK3576的Android14查看系统休眠时间
服务器·网络·数据库·rockchip
XPoet17 小时前
AI 编程工程化:Skill——给你的 AI 员工装上技能包
前端·后端·ai编程
XDHCOM17 小时前
ORA-32152报错咋整啊,数据库操作遇到null number问题远程帮忙修复
服务器·数据库·oracle
码事漫谈17 小时前
从“功能实现”到“深度优化”:金仓数据库连接条件下推技术的演进之路
后端
码事漫谈18 小时前
数据库查询优化中的谓词下推策略与成本感知优化实践
后端