常州网站建设书生商友,湖南省网站备案,自己做的网站只能打开一个链接,企业建站技术一、状态机
1. 定义
有限状态机#xff08;Finite-state machine, FSM#xff09;#xff0c;简称状态机#xff0c;是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
2. 组成要素 现态#xff08;src state#xff09;#xff1a;事务当前所处的状…一、状态机
1. 定义
有限状态机Finite-state machine, FSM简称状态机是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
2. 组成要素 现态src state事务当前所处的状态。 事件event事件就是执行某个操作的触发条件当一个事件被满足将会触发一个动作或者执行一次状态的迁移。 动作action事件满足后执行的动作。动作执行完毕后可以迁移到新的状态也可以仍旧保持原状态。动作不是必需的当事件满足后也可以不执行任何动作直接迁移到新状态。 次态dst state事件满足后要迁往的新状态。“次态”是相对于“现态”而言的“次态”一旦被激活就转变成新的“现态”了。 状态流转transition事物从现态转为次态的整个过程。
3. 优点
代码抽象将业务流程进行抽象和结构化将复杂的状态转移图分割成相邻状态的最小单元。这样相当于搭建乐高积木在这套机制上可以组合成复杂的状态转移图同时隐藏了系统的复杂度。简化流程业务rd只需要关注当前操作的业务逻辑状态流转过程中的业务回调函数极大的解耦了状态和业务。易扩展在新增状态或事件时无需修改原有的状态流转逻辑直接建立新的状态转移链路即可。业务建模通过最小粒度的相邻状态拼接最终组成了业务的整体graph。
二、代码 假设我们有要实现一个订单下单功能上图是订单状态的流转图方框为订单的状态箭头旁的文字为事件。
1. database包
package databaseimport 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 // 主键IDState 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 fsmimport (fmtreflectzuzhiang/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.transitionMapif 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.transitionMapevents, 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.DstStateaction : 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方法和transitionFuncif 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 orderimport (fmtzuzhiang/databasezuzhiang/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 orderimport (fmtzuzhiang/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 orderimport (fmtzuzhiang/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 orderimport (fmtzuzhiang/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 orderimport (fmtzuzhiang/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 mainimport (fmtzuzhiang/databasezuzhiang/order
)func main() {order.Init()orderList, dbErr : database.ListAllOrder()if dbErr ! nil {return}for _, curOrder : range orderList {params : make(map[string]interface{})params[order] curOrderif err : order.ExecOrderTask(params); err ! nil {fmt.Printf(执行订单任务出错%v\n, err)}fmt.Println(\n\n)}
}最后在main包里先初始化一个订单状态机查询所有订单并使用状态机执行订单任务推动订单状态转移。注意多个订单可以用同一个状态机来进行状态的迁移。
三、个人总结
为什么要使用状态机我想主要是它可以对一个复杂的业务流程进行模块化拆分使得代码更为易读。并且扩展性更好如果后续有新状态加入只需要在原来的基础上进行扩展即可甚至不需要了解整个业务流程。
其次它将数据库实体的状态流转进行了模范化避免了不同的开发人员在写更新数据库实体状态代码时可能导致的问题。