温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Go语言状态机如何实现

发布时间:2023-03-09 13:48:03 来源:亿速云 阅读:128 作者:iii 栏目:开发技术

这篇文章主要介绍“Go语言状态机如何实现”,在日常操作中,相信很多人在Go语言状态机如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Go语言状态机如何实现”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    一、状态机

    1. 定义

    有限状态机(Finite-state machine, FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

    2. 组成要素

    • 现态(src state):事务当前所处的状态。

    • 事件(event):事件就是执行某个操作的触发条件,当一个事件被满足,将会触发一个动作,或者执行一次状态的迁移。

    • 动作(action):事件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当事件满足后,也可以不执行任何动作,直接迁移到新状态。

    • 次态(dst state):事件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

    • 状态流转(transition):事物从现态转为次态的整个过程。

    3. 优点

    • 代码抽象:将业务流程进行抽象和结构化,将复杂的状态转移图,分割成相邻状态的最小单元。这样相当于搭建乐高积木,在这套机制上可以组合成复杂的状态转移图,同时隐藏了系统的复杂度。

    • 简化流程:业务rd只需要关注当前操作的业务逻辑(状态流转过程中的业务回调函数),极大的解耦了状态和业务。

    • 易扩展:在新增状态或事件时,无需修改原有的状态流转逻辑,直接建立新的状态转移链路即可。

    • 业务建模:通过最小粒度的相邻状态拼接,最终组成了业务的整体graph。

    二、代码

    Go语言状态机如何实现

    假设我们有要实现一个订单下单功能,上图是订单状态的流转图,方框为订单的状态,箭头旁的文字为事件。

    1. database包

    package database
    
    import "fmt"
    
    // DB 模拟数据库对象
    type DB struct {
    }
    
    // Transaction 模拟事务
    func (db *DB) Transaction(fun func() error) error {
        fmt.Println("事务执行开始。")
        err := fun()
        fmt.Println("事务执行结束。")
        return err
    }
    
    // Order 订单
    type Order struct {
        ID    int64 // 主键ID
        State int   // 状态
    }
    
    type OrderList []*Order
    
    // 查询所有订单
    func ListAllOrder() (OrderList, error) {
        orderList := OrderList{
            &Order{1, 0},
            &Order{2, 1},
            &Order{2, 2},
        }
        return orderList, nil
    }
    
    // UpdateOrderState 更新订单状态
    func UpdateOrderState(curOrder *Order, srcState int, dstState int) error {
        if curOrder.State == srcState {
            curOrder.State = dstState
        }
        fmt.Printf("更新id为 %v 的订单状态,从现态[%v]到次态[%v]\n", curOrder.ID, srcState, dstState)
        return nil
    }

    用来模拟数据库的操作,如数据库事务、查询所有订单、更新订单状态等。

    2. fsm包

    package fsm
    
    import (
        "fmt"
        "reflect"
        "zuzhiang/database"
    )
    
    // FSMState 状态机的状态类型
    type FSMState int
    
    // FSMEvent 状态机的事件类型
    type FSMEvent string
    
    // FSMTransitionMap 状态机的状态转移图类型,现态和事件一旦确定,次态和动作就唯一确定
    type FSMTransitionMap map[FSMState]map[FSMEvent]FSMDstStateAndAction
    
    // FSMTransitionFunc 状态机的状态转移函数类型
    type FSMTransitionFunc func(params map[string]interface{}, srcState FSMState, dstState FSMState) error
    
    // FSMDstStateAndAction 状态机的次态和动作
    type FSMDstStateAndAction struct {
        DstState FSMState  // 次态
        Action   FSMAction // 动作
    }
    
    // FSMAction 状态机的动作
    type FSMAction interface {
        Before(bizParams map[string]interface{}) error                   // 状态转移前执行
        Execute(bizParams map[string]interface{}, tx *database.DB) error // 状态转移中执行
        After(bizParams map[string]interface{}) error                    // 状态转移后执行
    }
    
    // FSM 状态机,元素均为不可导出
    type FSM struct {
        transitionMap  FSMTransitionMap  // 状态转移图
        transitionFunc FSMTransitionFunc // 状态转移函数
    }
    
    // CreateNewFSM 创建一个新的状态机
    func CreateNewFSM(transitionFunc FSMTransitionFunc) *FSM {
        return &FSM{
            transitionMap:  make(FSMTransitionMap),
            transitionFunc: transitionFunc,
        }
    }
    
    // SetTransitionMap 设置状态机的状态转移图
    func (fsm *FSM) SetTransitionMap(srcState FSMState, event FSMEvent, dstState FSMState, action FSMAction) {
        if int(srcState) < 0 || len(event) <= 0 || int(dstState) < 0 {
            panic("现态|事件|次态非法。")
            return
        }
        transitionMap := fsm.transitionMap
        if transitionMap == nil {
            transitionMap = make(FSMTransitionMap)
        }
        if _, ok := transitionMap[srcState]; !ok {
            transitionMap[srcState] = make(map[FSMEvent]FSMDstStateAndAction)
        }
        if _, ok := transitionMap[srcState][event]; !ok {
            dstStateAndAction := FSMDstStateAndAction{
                DstState: dstState,
                Action:   action,
            }
            transitionMap[srcState][event] = dstStateAndAction
        } else {
            fmt.Printf("现态[%v]+事件[%v]+次态[%v]已定义过,请勿重复定义。\n", srcState, event, dstState)
            return
        }
        fsm.transitionMap = transitionMap
    }
    
    // Push 状态机的状态迁移
    func (fsm *FSM) Push(tx *database.DB, params map[string]interface{}, currentState FSMState, event FSMEvent) error {
        // 根据现态和事件从状态转移图获取次态和动作
        transitionMap := fsm.transitionMap
        events, eventExist := transitionMap[currentState]
        if !eventExist {
            return fmt.Errorf("现态[%v]未配置迁移事件", currentState)
        }
        dstStateAndAction, ok := events[event]
        if !ok {
            return fmt.Errorf("现态[%v]+迁移事件[%v]未配置次态", currentState, event)
        }
        dstState := dstStateAndAction.DstState
        action := dstStateAndAction.Action
    
        // 执行before方法
        if action != nil {
            fsmActionName := reflect.ValueOf(action).String()
            fmt.Printf("现态[%v]+迁移事件[%v]->次态[%v], [%v].before\n", currentState, event, dstState, fsmActionName)
            if err := action.Before(params); err != nil {
                return fmt.Errorf("现态[%v]+迁移事件[%v]->次态[%v]失败, [%v].before, err: %v", currentState, event, dstState, fsmActionName, err)
            }
        }
    
        // 事务执行execute方法和transitionFunc
        if tx == nil {
            tx = new(database.DB)
        }
        transactionErr := tx.Transaction(func() error {
            fsmActionName := reflect.ValueOf(action).String()
            fmt.Printf("现态[%v]+迁移事件[%v]->次态[%v], [%v].execute\n", currentState, event, dstState, fsmActionName)
            if action != nil {
                if err := action.Execute(params, tx); err != nil {
                    return fmt.Errorf("状态转移执行出错:%v", err)
                }
            }
    
            fmt.Printf("现态[%v]+迁移事件[%v]->次态[%v], transitionFunc\n", currentState, event, dstState)
            if err := fsm.transitionFunc(params, currentState, dstState); err != nil {
                return fmt.Errorf("执行状态转移函数出错: %v", err)
            }
            return nil
        })
        if transactionErr != nil {
            return transactionErr
        }
    
        // 执行after方法
        if action != nil {
            fsmActionName := reflect.ValueOf(action).String()
            fmt.Printf("现态[%v]+迁移事件[%v]->次态[%v], [%v].after\n", currentState, event, dstState, fsmActionName)
            if err := action.After(params); err != nil {
                return fmt.Errorf("现态[%v]+迁移事件[%v]->次态[%v]失败, [%v].before, err: %v", currentState, event, dstState, fsmActionName, err)
            }
        }
        return nil
    }

    状态机包含的元素有两个:状态转移图和状态转移函数,为了防止包外直接调用,这两个元素都设为了不可导出的。状态转移图说明了状态机的状态流转情况,状态转移函数定义了在状态转移的过程中需要做的事情,在创建状态时指定,如更新数据库实体(order)的状态。

    而状态转移图又包含现态、事件、次态和动作,一旦现态和事件确定,那么状态流转的唯一次态和动作就随之确定。

    状态机的动作又包含三个:Before、Execute和After。Before操作在是事务前执行,由于此时没有翻状态,所以该步可能会被重复执行。Execute操作是和状态转移函数在同一事务中执行的,同时成功或同时失败。After操作是在事务后执行,因为在执行前状态已经翻转,所以最多会执行一次,在业务上允许执行失败或未执行。

    状态机的主要方法有两个,SetTransitionMap方法用来设置状态机的状态转移图,Push方法用来根据现态和事件推动状态机进行状态翻转。Push方法中会先执行Before动作,再在同一事务中执行Execute动作和状态转移函数,最后执行After动作。在执行Push方法的时候会将params参数传递给状态转移函数。

    3. order包

    (1) order

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
        "zuzhiang/fsm"
    )
    
    var (
        // 状态
        StateOrderInit          = fsm.FSMState(0) // 初始状态
        StateOrderToBePaid      = fsm.FSMState(1) // 待支付
        StateOrderToBeDelivered = fsm.FSMState(2) // 待发货
        StateOrderCancel        = fsm.FSMState(3) // 订单取消
        StateOrderToBeReceived  = fsm.FSMState(4) // 待收货
        StateOrderDone          = fsm.FSMState(5) // 订单完成
    
        // 事件
        EventOrderPlace      = fsm.FSMEvent("EventOrderPlace")      // 下单
        EventOrderPay        = fsm.FSMEvent("EventOrderPay")        // 支付
        EventOrderPayTimeout = fsm.FSMEvent("EventOrderPayTimeout") // 支付超时
        EventOrderDeliver    = fsm.FSMEvent("EventOrderDeliver")    // 发货
        EventOrderReceive    = fsm.FSMEvent("EventOrderReceive")    // 收货
    )
    
    var orderFSM *fsm.FSM
    
    // orderTransitionFunc 订单状态转移函数
    func orderTransitionFunc(params map[string]interface{}, srcState fsm.FSMState, dstState fsm.FSMState) error {
        // 从params中解析order参数
        key, ok := params["order"]
        if !ok {
            return fmt.Errorf("params[\"order\"]不存在。")
        }
        curOrder := key.(*database.Order)
        fmt.Printf("order.ID: %v, order.State: %v\n", curOrder.ID, curOrder.State)
    
        // 订单状态转移
        if err := database.UpdateOrderState(curOrder, int(srcState), int(dstState)); err != nil {
            return err
        }
        return nil
    }
    
    // Init 状态机的状态转移图初始化
    func Init() {
        orderFSM = fsm.CreateNewFSM(orderTransitionFunc)
        orderFSM.SetTransitionMap(StateOrderInit, EventOrderPlace, StateOrderToBePaid, PlaceAction{})                  // 初始化+下单 -> 待支付
        orderFSM.SetTransitionMap(StateOrderToBePaid, EventOrderPay, StateOrderToBeDelivered, PayAction{})             // 待支付+支付 -> 待发货
        orderFSM.SetTransitionMap(StateOrderToBePaid, EventOrderPayTimeout, StateOrderCancel, nil)                     // 待支付+支付超时 -> 订单取消
        orderFSM.SetTransitionMap(StateOrderToBeDelivered, EventOrderDeliver, StateOrderToBeReceived, DeliverAction{}) // 待发货+发货 -> 待收货
        orderFSM.SetTransitionMap(StateOrderToBeReceived, EventOrderReceive, StateOrderDone, ReceiveAction{})          // 待收货+收货 -> 订单完成
    }
    
    // ExecOrderTask 执行订单任务,推动状态转移
    func ExecOrderTask(params map[string]interface{}) error {
        // 从params中解析order参数
        key, ok := params["order"]
        if !ok {
            return fmt.Errorf("params[\"order\"]不存在。")
        }
        curOrder := key.(*database.Order)
    
        // 初始化+下单 -> 待支付
        if curOrder.State == int(StateOrderInit) {
            if err := orderFSM.Push(nil, params, StateOrderInit, EventOrderPlace); err != nil {
                return err
            }
        }
        // 待支付+支付 -> 待发货
        if curOrder.State == int(StateOrderToBePaid) {
            if err := orderFSM.Push(nil, params, StateOrderToBePaid, EventOrderPay); err != nil {
                return err
            }
        }
        // 待支付+支付超时 -> 订单取消
        if curOrder.State == int(StateOrderToBePaid) {
            if err := orderFSM.Push(nil, params, StateOrderToBePaid, EventOrderPayTimeout); err != nil {
                return err
            }
        }
        // 待发货+发货 -> 待收货
        if curOrder.State == int(StateOrderToBeDelivered) {
            if err := orderFSM.Push(nil, params, StateOrderToBeDelivered, EventOrderDeliver); err != nil {
                return err
            }
        }
        // 待收货+收货 -> 订单完成
        if curOrder.State == int(StateOrderToBeReceived) {
            if err := orderFSM.Push(nil, params, StateOrderToBeReceived, EventOrderReceive); err != nil {
                return err
            }
        }
        return nil
    }

    order包中做的事情主要有:

    • 定义订单状态机的状态和事件;

    • 创建一个状态机,并设置状态转移函数和状态转移图;

    • 执行订单任务,推动状态转移。

    (2) order_action_place

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type PlaceAction struct {
    }
    
    // Before 事务前执行,业务上允许多次操作
    func (receiver PlaceAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("执行下单的Before方法。")
        return nil
    }
    
    // Execute 事务中执行,与状态转移在同一事务中
    func (receiver PlaceAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("执行下单的Execute方法。")
        return nil
    }
    
    // After 事务后执行,业务上允许执行失败或未执行
    func (receiver PlaceAction) After(bizParams map[string]interface{}) error {
        fmt.Println("执行下单的After方法。")
        return nil
    }

    (2) ~ (5)是订单不同动作的声明和实现。

    (3) order_action_pay

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type PayAction struct {
    }
    
    // Before 事务前执行,业务上允许多次操作
    func (receiver PayAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("执行支付的Before方法。")
        return nil
    }
    
    // Execute 事务中执行,与状态转移在同一事务中
    func (receiver PayAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("执行支付的Execute方法。")
        return nil
    }
    
    // After 事务后执行,业务上允许执行失败或未执行
    func (receiver PayAction) After(bizParams map[string]interface{}) error {
        fmt.Println("执行支付的After方法。")
        return nil
    }

    (4) order_action_deliver

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type DeliverAction struct {
    }
    
    // Before 事务前执行,业务上允许多次操作
    func (receiver DeliverAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("执行发货的Before方法。")
        return nil
    }
    
    // Execute 事务中执行,与状态转移在同一事务中
    func (receiver DeliverAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("执行发货的Execute方法。")
        return nil
    }
    
    // After 事务后执行,业务上允许执行失败或未执行
    func (receiver DeliverAction) After(bizParams map[string]interface{}) error {
        fmt.Println("执行发货的After方法。")
        return nil
    }

    (5) order_action_receive

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type ReceiveAction struct {
    }
    
    // Before 事务前执行,业务上允许多次操作
    func (receiver ReceiveAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("执行收货的Before方法。")
        return nil
    }
    
    // Execute 事务中执行,与状态转移在同一事务中
    func (receiver ReceiveAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("执行收货的Execute方法。")
        return nil
    }
    
    // After 事务后执行,业务上允许执行失败或未执行
    func (receiver ReceiveAction) After(bizParams map[string]interface{}) error {
        fmt.Println("执行收货的After方法。")
        return nil
    }

    4. main包

    package main
    
    import (
        "fmt"
        "zuzhiang/database"
        "zuzhiang/order"
    )
    
    func main() {
        order.Init()
        orderList, dbErr := database.ListAllOrder()
        if dbErr != nil {
            return
        }
        for _, curOrder := range orderList {
            params := make(map[string]interface{})
            params["order"] = curOrder
            if err := order.ExecOrderTask(params); err != nil {
                fmt.Printf("执行订单任务出错:%v\n", err)
            }
            fmt.Println("\n\n")
        }
    }

    最后在main包里先初始化一个订单状态机,查询所有订单,并使用状态机执行订单任务,推动订单状态转移。注意多个订单可以用同一个状态机来进行状态的迁移。

    到此,关于“Go语言状态机如何实现”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI