溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言狀態(tài)機如何實現(xiàn)

發(fā)布時間:2023-03-09 13:48:03 來源:億速云 閱讀:118 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Go語言狀態(tài)機如何實現(xiàn)”,在日常操作中,相信很多人在Go語言狀態(tài)機如何實現(xiàn)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Go語言狀態(tài)機如何實現(xiàn)”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    一、狀態(tài)機

    1. 定義

    有限狀態(tài)機(Finite-state machine, FSM),簡稱狀態(tài)機,是表示有限個狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學模型。

    2. 組成要素

    • 現(xiàn)態(tài)(src state):事務當前所處的狀態(tài)。

    • 事件(event):事件就是執(zhí)行某個操作的觸發(fā)條件,當一個事件被滿足,將會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)的遷移。

    • 動作(action):事件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。動作不是必需的,當事件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(tài)。

    • 次態(tài)(dst state):事件滿足后要遷往的新狀態(tài)?!按螒B(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。

    • 狀態(tài)流轉(zhuǎn)(transition):事物從現(xiàn)態(tài)轉(zhuǎn)為次態(tài)的整個過程。

    3. 優(yōu)點

    • 代碼抽象:將業(yè)務流程進行抽象和結(jié)構(gòu)化,將復雜的狀態(tài)轉(zhuǎn)移圖,分割成相鄰狀態(tài)的最小單元。這樣相當于搭建樂高積木,在這套機制上可以組合成復雜的狀態(tài)轉(zhuǎn)移圖,同時隱藏了系統(tǒng)的復雜度。

    • 簡化流程:業(yè)務rd只需要關(guān)注當前操作的業(yè)務邏輯(狀態(tài)流轉(zhuǎn)過程中的業(yè)務回調(diào)函數(shù)),極大的解耦了狀態(tài)和業(yè)務。

    • 易擴展:在新增狀態(tài)或事件時,無需修改原有的狀態(tài)流轉(zhuǎn)邏輯,直接建立新的狀態(tài)轉(zhuǎn)移鏈路即可。

    • 業(yè)務建模:通過最小粒度的相鄰狀態(tài)拼接,最終組成了業(yè)務的整體graph。

    二、代碼

    Go語言狀態(tài)機如何實現(xiàn)

    假設(shè)我們有要實現(xiàn)一個訂單下單功能,上圖是訂單狀態(tài)的流轉(zhuǎn)圖,方框為訂單的狀態(tài),箭頭旁的文字為事件。

    1. database包

    package database
    
    import "fmt"
    
    // DB 模擬數(shù)據(jù)庫對象
    type DB struct {
    }
    
    // Transaction 模擬事務
    func (db *DB) Transaction(fun func() error) error {
        fmt.Println("事務執(zhí)行開始。")
        err := fun()
        fmt.Println("事務執(zhí)行結(jié)束。")
        return err
    }
    
    // Order 訂單
    type Order struct {
        ID    int64 // 主鍵ID
        State int   // 狀態(tài)
    }
    
    type OrderList []*Order
    
    // 查詢所有訂單
    func ListAllOrder() (OrderList, error) {
        orderList := OrderList{
            &Order{1, 0},
            &Order{2, 1},
            &Order{2, 2},
        }
        return orderList, nil
    }
    
    // UpdateOrderState 更新訂單狀態(tài)
    func UpdateOrderState(curOrder *Order, srcState int, dstState int) error {
        if curOrder.State == srcState {
            curOrder.State = dstState
        }
        fmt.Printf("更新id為 %v 的訂單狀態(tài),從現(xiàn)態(tài)[%v]到次態(tài)[%v]\n", curOrder.ID, srcState, dstState)
        return nil
    }

    用來模擬數(shù)據(jù)庫的操作,如數(shù)據(jù)庫事務、查詢所有訂單、更新訂單狀態(tài)等。

    2. fsm包

    package fsm
    
    import (
        "fmt"
        "reflect"
        "zuzhiang/database"
    )
    
    // FSMState 狀態(tài)機的狀態(tài)類型
    type FSMState int
    
    // FSMEvent 狀態(tài)機的事件類型
    type FSMEvent string
    
    // FSMTransitionMap 狀態(tài)機的狀態(tài)轉(zhuǎn)移圖類型,現(xiàn)態(tài)和事件一旦確定,次態(tài)和動作就唯一確定
    type FSMTransitionMap map[FSMState]map[FSMEvent]FSMDstStateAndAction
    
    // FSMTransitionFunc 狀態(tài)機的狀態(tài)轉(zhuǎn)移函數(shù)類型
    type FSMTransitionFunc func(params map[string]interface{}, srcState FSMState, dstState FSMState) error
    
    // FSMDstStateAndAction 狀態(tài)機的次態(tài)和動作
    type FSMDstStateAndAction struct {
        DstState FSMState  // 次態(tài)
        Action   FSMAction // 動作
    }
    
    // FSMAction 狀態(tài)機的動作
    type FSMAction interface {
        Before(bizParams map[string]interface{}) error                   // 狀態(tài)轉(zhuǎn)移前執(zhí)行
        Execute(bizParams map[string]interface{}, tx *database.DB) error // 狀態(tài)轉(zhuǎn)移中執(zhí)行
        After(bizParams map[string]interface{}) error                    // 狀態(tài)轉(zhuǎn)移后執(zhí)行
    }
    
    // FSM 狀態(tài)機,元素均為不可導出
    type FSM struct {
        transitionMap  FSMTransitionMap  // 狀態(tài)轉(zhuǎn)移圖
        transitionFunc FSMTransitionFunc // 狀態(tài)轉(zhuǎn)移函數(shù)
    }
    
    // CreateNewFSM 創(chuàng)建一個新的狀態(tài)機
    func CreateNewFSM(transitionFunc FSMTransitionFunc) *FSM {
        return &FSM{
            transitionMap:  make(FSMTransitionMap),
            transitionFunc: transitionFunc,
        }
    }
    
    // SetTransitionMap 設(shè)置狀態(tài)機的狀態(tài)轉(zhuǎn)移圖
    func (fsm *FSM) SetTransitionMap(srcState FSMState, event FSMEvent, dstState FSMState, action FSMAction) {
        if int(srcState) < 0 || len(event) <= 0 || int(dstState) < 0 {
            panic("現(xiàn)態(tài)|事件|次態(tài)非法。")
            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("現(xiàn)態(tài)[%v]+事件[%v]+次態(tài)[%v]已定義過,請勿重復定義。\n", srcState, event, dstState)
            return
        }
        fsm.transitionMap = transitionMap
    }
    
    // Push 狀態(tài)機的狀態(tài)遷移
    func (fsm *FSM) Push(tx *database.DB, params map[string]interface{}, currentState FSMState, event FSMEvent) error {
        // 根據(jù)現(xiàn)態(tài)和事件從狀態(tài)轉(zhuǎn)移圖獲取次態(tài)和動作
        transitionMap := fsm.transitionMap
        events, eventExist := transitionMap[currentState]
        if !eventExist {
            return fmt.Errorf("現(xiàn)態(tài)[%v]未配置遷移事件", currentState)
        }
        dstStateAndAction, ok := events[event]
        if !ok {
            return fmt.Errorf("現(xiàn)態(tài)[%v]+遷移事件[%v]未配置次態(tài)", currentState, event)
        }
        dstState := dstStateAndAction.DstState
        action := dstStateAndAction.Action
    
        // 執(zhí)行before方法
        if action != nil {
            fsmActionName := reflect.ValueOf(action).String()
            fmt.Printf("現(xiàn)態(tài)[%v]+遷移事件[%v]->次態(tài)[%v], [%v].before\n", currentState, event, dstState, fsmActionName)
            if err := action.Before(params); err != nil {
                return fmt.Errorf("現(xiàn)態(tài)[%v]+遷移事件[%v]->次態(tài)[%v]失敗, [%v].before, err: %v", currentState, event, dstState, fsmActionName, err)
            }
        }
    
        // 事務執(zhí)行execute方法和transitionFunc
        if tx == nil {
            tx = new(database.DB)
        }
        transactionErr := tx.Transaction(func() error {
            fsmActionName := reflect.ValueOf(action).String()
            fmt.Printf("現(xiàn)態(tài)[%v]+遷移事件[%v]->次態(tài)[%v], [%v].execute\n", currentState, event, dstState, fsmActionName)
            if action != nil {
                if err := action.Execute(params, tx); err != nil {
                    return fmt.Errorf("狀態(tài)轉(zhuǎn)移執(zhí)行出錯:%v", err)
                }
            }
    
            fmt.Printf("現(xiàn)態(tài)[%v]+遷移事件[%v]->次態(tài)[%v], transitionFunc\n", currentState, event, dstState)
            if err := fsm.transitionFunc(params, currentState, dstState); err != nil {
                return fmt.Errorf("執(zhí)行狀態(tài)轉(zhuǎn)移函數(shù)出錯: %v", err)
            }
            return nil
        })
        if transactionErr != nil {
            return transactionErr
        }
    
        // 執(zhí)行after方法
        if action != nil {
            fsmActionName := reflect.ValueOf(action).String()
            fmt.Printf("現(xiàn)態(tài)[%v]+遷移事件[%v]->次態(tài)[%v], [%v].after\n", currentState, event, dstState, fsmActionName)
            if err := action.After(params); err != nil {
                return fmt.Errorf("現(xiàn)態(tài)[%v]+遷移事件[%v]->次態(tài)[%v]失敗, [%v].before, err: %v", currentState, event, dstState, fsmActionName, err)
            }
        }
        return nil
    }

    狀態(tài)機包含的元素有兩個:狀態(tài)轉(zhuǎn)移圖和狀態(tài)轉(zhuǎn)移函數(shù),為了防止包外直接調(diào)用,這兩個元素都設(shè)為了不可導出的。狀態(tài)轉(zhuǎn)移圖說明了狀態(tài)機的狀態(tài)流轉(zhuǎn)情況,狀態(tài)轉(zhuǎn)移函數(shù)定義了在狀態(tài)轉(zhuǎn)移的過程中需要做的事情,在創(chuàng)建狀態(tài)時指定,如更新數(shù)據(jù)庫實體(order)的狀態(tài)。

    而狀態(tài)轉(zhuǎn)移圖又包含現(xiàn)態(tài)、事件、次態(tài)和動作,一旦現(xiàn)態(tài)和事件確定,那么狀態(tài)流轉(zhuǎn)的唯一次態(tài)和動作就隨之確定。

    狀態(tài)機的動作又包含三個:Before、Execute和After。Before操作在是事務前執(zhí)行,由于此時沒有翻狀態(tài),所以該步可能會被重復執(zhí)行。Execute操作是和狀態(tài)轉(zhuǎn)移函數(shù)在同一事務中執(zhí)行的,同時成功或同時失敗。After操作是在事務后執(zhí)行,因為在執(zhí)行前狀態(tài)已經(jīng)翻轉(zhuǎn),所以最多會執(zhí)行一次,在業(yè)務上允許執(zhí)行失敗或未執(zhí)行。

    狀態(tài)機的主要方法有兩個,SetTransitionMap方法用來設(shè)置狀態(tài)機的狀態(tài)轉(zhuǎn)移圖,Push方法用來根據(jù)現(xiàn)態(tài)和事件推動狀態(tài)機進行狀態(tài)翻轉(zhuǎn)。Push方法中會先執(zhí)行Before動作,再在同一事務中執(zhí)行Execute動作和狀態(tài)轉(zhuǎn)移函數(shù),最后執(zhí)行After動作。在執(zhí)行Push方法的時候會將params參數(shù)傳遞給狀態(tài)轉(zhuǎn)移函數(shù)。

    3. order包

    (1) order

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
        "zuzhiang/fsm"
    )
    
    var (
        // 狀態(tài)
        StateOrderInit          = fsm.FSMState(0) // 初始狀態(tài)
        StateOrderToBePaid      = fsm.FSMState(1) // 待支付
        StateOrderToBeDelivered = fsm.FSMState(2) // 待發(fā)貨
        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")    // 發(fā)貨
        EventOrderReceive    = fsm.FSMEvent("EventOrderReceive")    // 收貨
    )
    
    var orderFSM *fsm.FSM
    
    // orderTransitionFunc 訂單狀態(tài)轉(zhuǎn)移函數(shù)
    func orderTransitionFunc(params map[string]interface{}, srcState fsm.FSMState, dstState fsm.FSMState) error {
        // 從params中解析order參數(shù)
        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)
    
        // 訂單狀態(tài)轉(zhuǎn)移
        if err := database.UpdateOrderState(curOrder, int(srcState), int(dstState)); err != nil {
            return err
        }
        return nil
    }
    
    // Init 狀態(tài)機的狀態(tài)轉(zhuǎn)移圖初始化
    func Init() {
        orderFSM = fsm.CreateNewFSM(orderTransitionFunc)
        orderFSM.SetTransitionMap(StateOrderInit, EventOrderPlace, StateOrderToBePaid, PlaceAction{})                  // 初始化+下單 -> 待支付
        orderFSM.SetTransitionMap(StateOrderToBePaid, EventOrderPay, StateOrderToBeDelivered, PayAction{})             // 待支付+支付 -> 待發(fā)貨
        orderFSM.SetTransitionMap(StateOrderToBePaid, EventOrderPayTimeout, StateOrderCancel, nil)                     // 待支付+支付超時 -> 訂單取消
        orderFSM.SetTransitionMap(StateOrderToBeDelivered, EventOrderDeliver, StateOrderToBeReceived, DeliverAction{}) // 待發(fā)貨+發(fā)貨 -> 待收貨
        orderFSM.SetTransitionMap(StateOrderToBeReceived, EventOrderReceive, StateOrderDone, ReceiveAction{})          // 待收貨+收貨 -> 訂單完成
    }
    
    // ExecOrderTask 執(zhí)行訂單任務,推動狀態(tài)轉(zhuǎn)移
    func ExecOrderTask(params map[string]interface{}) error {
        // 從params中解析order參數(shù)
        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
            }
        }
        // 待支付+支付 -> 待發(fā)貨
        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
            }
        }
        // 待發(fā)貨+發(fā)貨 -> 待收貨
        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包中做的事情主要有:

    • 定義訂單狀態(tài)機的狀態(tài)和事件;

    • 創(chuàng)建一個狀態(tài)機,并設(shè)置狀態(tài)轉(zhuǎn)移函數(shù)和狀態(tài)轉(zhuǎn)移圖;

    • 執(zhí)行訂單任務,推動狀態(tài)轉(zhuǎn)移。

    (2) order_action_place

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type PlaceAction struct {
    }
    
    // Before 事務前執(zhí)行,業(yè)務上允許多次操作
    func (receiver PlaceAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行下單的Before方法。")
        return nil
    }
    
    // Execute 事務中執(zhí)行,與狀態(tài)轉(zhuǎn)移在同一事務中
    func (receiver PlaceAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("執(zhí)行下單的Execute方法。")
        return nil
    }
    
    // After 事務后執(zhí)行,業(yè)務上允許執(zhí)行失敗或未執(zhí)行
    func (receiver PlaceAction) After(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行下單的After方法。")
        return nil
    }

    (2) ~ (5)是訂單不同動作的聲明和實現(xiàn)。

    (3) order_action_pay

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type PayAction struct {
    }
    
    // Before 事務前執(zhí)行,業(yè)務上允許多次操作
    func (receiver PayAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行支付的Before方法。")
        return nil
    }
    
    // Execute 事務中執(zhí)行,與狀態(tài)轉(zhuǎn)移在同一事務中
    func (receiver PayAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("執(zhí)行支付的Execute方法。")
        return nil
    }
    
    // After 事務后執(zhí)行,業(yè)務上允許執(zhí)行失敗或未執(zhí)行
    func (receiver PayAction) After(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行支付的After方法。")
        return nil
    }

    (4) order_action_deliver

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type DeliverAction struct {
    }
    
    // Before 事務前執(zhí)行,業(yè)務上允許多次操作
    func (receiver DeliverAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行發(fā)貨的Before方法。")
        return nil
    }
    
    // Execute 事務中執(zhí)行,與狀態(tài)轉(zhuǎn)移在同一事務中
    func (receiver DeliverAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("執(zhí)行發(fā)貨的Execute方法。")
        return nil
    }
    
    // After 事務后執(zhí)行,業(yè)務上允許執(zhí)行失敗或未執(zhí)行
    func (receiver DeliverAction) After(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行發(fā)貨的After方法。")
        return nil
    }

    (5) order_action_receive

    package order
    
    import (
        "fmt"
        "zuzhiang/database"
    )
    
    type ReceiveAction struct {
    }
    
    // Before 事務前執(zhí)行,業(yè)務上允許多次操作
    func (receiver ReceiveAction) Before(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行收貨的Before方法。")
        return nil
    }
    
    // Execute 事務中執(zhí)行,與狀態(tài)轉(zhuǎn)移在同一事務中
    func (receiver ReceiveAction) Execute(bizParams map[string]interface{}, tx *database.DB) error {
        fmt.Println("執(zhí)行收貨的Execute方法。")
        return nil
    }
    
    // After 事務后執(zhí)行,業(yè)務上允許執(zhí)行失敗或未執(zhí)行
    func (receiver ReceiveAction) After(bizParams map[string]interface{}) error {
        fmt.Println("執(zhí)行收貨的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("執(zhí)行訂單任務出錯:%v\n", err)
            }
            fmt.Println("\n\n")
        }
    }

    最后在main包里先初始化一個訂單狀態(tài)機,查詢所有訂單,并使用狀態(tài)機執(zhí)行訂單任務,推動訂單狀態(tài)轉(zhuǎn)移。注意多個訂單可以用同一個狀態(tài)機來進行狀態(tài)的遷移。

    到此,關(guān)于“Go語言狀態(tài)機如何實現(xiàn)”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI