您好,登錄后才能下訂單哦!
這篇文章主要介紹“Go語言狀態(tài)機如何實現(xiàn)”,在日常操作中,相信很多人在Go語言狀態(tài)機如何實現(xiàn)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Go語言狀態(tài)機如何實現(xiàn)”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
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。
假設(shè)我們有要實現(xiàn)一個訂單下單功能,上圖是訂單狀態(tài)的流轉(zhuǎn)圖,方框為訂單的狀態(tài),箭頭旁的文字為事件。
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)等。
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ù)。
(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 }
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>
免責聲明:本站發(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)容。