泥球型狀態機:對於電商類的系統、遊戲和公司內部流程系統來說,最複雜的莫過於處理其中的狀態扭轉。
如我公司的訂單購買系統:
預約=>審核=>打款=>上傳憑條=>憑條審核=>打款核驗=>返傭對賬=>返傭憑條審核=>返傭
這期間,每乙個狀態有包括了,「通過」 / 「拒絕」的操作。
通常來說,面對這樣的需求,最容易想到的解決方案就是,定義不同的列舉值,不同狀態之間的扭轉就使用if-else或者switch-case來做判斷。如下面所示,根據前端傳遞過來的不同操作值 orderop來做狀態操作判斷,進行不同的狀態處理。
switch (orderop) 對於短期來說,這個邏輯並沒有什麼不對,但是,隨著需求不斷的增加,操作許可權的安全校驗分離,它的跳轉就會變的不可維護。然後,我們的系統狀態扭轉,就像似乙個泥球,如下圖所示:
我們分析一下上面的**邏輯缺陷:
1.多餘的orderop操作
將所有的狀態扭轉操作向前端暴露,如果能夠猜對狀態的列舉值,理論上是可以從任意狀態跳轉到任意狀態。
過多的狀態判斷過多的狀態,會不斷的增加switch-case和if-else判斷,**上難以閱讀,後期開發風險過大。
無法控制許可權其實對於每乙個狀態的操作流程來說,並不是都具有跳轉到下乙個狀態的許可權的。比如,只有先購買才能付款、先付款才能發貨等等。然而,我們在許可權審核的時候,不可能每乙個state都去review。
根據存在的問題,尋找對應的解決方案。
分析每乙個狀態的操作方式所有的操作都只有兩種角色
-操作者
- 修改狀態屬性
- 取消
-審核者
- 通過
- 不通過
其實,這麼一看每乙個狀態的操作還可以列舉出來的,我們稱之為:有限狀態。
定義狀態扭轉狀態的跳轉不再由swich-case來做操作,在乙個固定的地方定義下乙個操作,和與操作匹配的下乙個狀,一目了然。
狀態分層狀態越來越多,我們可以對這麼多狀態進行乙個歸類。如,
-購買前
- 預約狀態
- 預約審核狀態
- 付款
- 已付款
-購買中
- 待發貨
- 待收貨
- 待評價
-購買後
- 售後投訴
- 投訴處理
對於每乙個上層狀態來說,我們不用過多的關心它的子狀態在其中如何扭轉。只需要定義,如只有在「購買前-已付款」狀態才能跳轉到「購買中」。「購買前」狀態中的子狀態如何跳轉到購買前不用思考。
即如下所示:
其實對於整個設計模式來說,這種資料機構有乙個專有的名詞————狀態機。狀態機是ai中常用的一種架構,有很多中實現方式。而且實現簡單,甚至用乙個switch-case就可以了;然而對於多流程結構化的狀態操作是有乙個專有的設計方式:層次化狀態機(hfsm)。
定義狀態分別定義訂單中的每乙個狀態的列舉,並設定歸屬的上層狀態
/*** 訂單狀態列舉
* * @author zhoushengtao
* @since 2016/12/18.
*/public enum orderstateenum
public iorderaction getorderaction()
public int getorderno()
public ordersectionenum getordersectionenum()
}為了方便區分,我們又叫上層為乙個「環節」
/***
* 訂單環節列舉
* * @author zhoushengtao
* @since 2016-12-18 16:48:10
*/public enum ordersectionenum
/*** 獲取該環節的入口狀態
* @return 入口狀態值
*/public int getentranceorderno()
}將訂單型別切換制定統一入口/**
* 訂單狀態機操作單例
* * @author zhoushengtao
* @since 2016/12/17.
*/public class orderstatemachine extends observable
/*** 單例
* @param order 訂單
* @return ordersection
*/public static orderstatemachine getinstance(order order)
sordersection.setstate(orderstateenum.getstate(order.getorderno()));
sordersection.mcontext.setorder(order);
sordersection.mcontext.setordersection(sordersection);
assert(sordersection.getstate() != null);
return sordersection;
}/**
* 修改
*/public void modify()
/*** 取消
*/public void cancel()
/*** 審核通過
*/public void auditpass()
/*** 審核失敗
*/public void auditfailure()
/*** 獲取當前狀態
*/private orderstateenum getstate()
/*** 設定新狀態
* @param newstate 新狀態
*/private void setstate(orderstateenum newstate)
/*** 切換新狀態
* @param newstate 新狀態
*/public void transnewstate(orderstateenum newstate)
} else
notifyobservers(newstate);}}
}抽象每乙個列舉的操作/**
* 確認訂單狀態
* * @author zhoushengtao
* @since 2016/12/18.
*/public class orderconfirmaction extends iorderaction
@override
public void cancel(orderstatecontext context)
@override
public void auditpass(orderstatecontext context)
@override
public void auditfailure(orderstatecontext context)
}測試操作public static void main(string args) 此**demo是從專案裡面剝離出來的,沒有什麼實際的邏輯,韜哥我放到github上:
共大家參考學習,寫的不對的地方請多包涵。
要為狀態定義入口 / 出口 要詳細設計每乙個狀態,列出層次化狀態結構,利於分工理解 每乙個子狀態操作邏輯少,不易出錯 狀態列舉定義,將列舉值、操作、跳轉限制合為一體,清晰明了。大規模支援較差 高併發容易出錯(需要加入操作佇列) 沒有書寫狀態切換log(後期排查很重要) 沒有動態化設定邏輯,遊戲設計中往往是讓遊戲策劃可以動態的修改狀態的切換邏輯。
FPGA 狀態機的模型之Moore型狀態機
上篇博文 狀態機,fpga的靈魂,說到了狀態機的基礎知識,講到了狀態機的組成六要素,工作四要素。這篇博文來講狀態機的模型之moore狀態機,從標題也能看出,狀態機的知識並沒有結束,後面還會提到mealy型狀態機。根據狀態機的輸出與其現態 輸入之間的關係,可將fpga中的狀態機抽象為三種基本模型 mo...
FPGA 狀態機的模型之Mealy型狀態機
上篇博文講了 moore型狀態機,這篇博文和上篇博文思路一致,如果讀懂了上篇博文,這篇博文就很容易理解了。如果乙個狀態機的輸出是由現態和輸入共同決定的,那麼它就是乙個mealy型的狀態機。而按照驅動輸出的數位電路特性,又將mealy型狀態機細分為mealy 1型 mealy 2型 mealy 3型,...
狀態機 狀態機0
近半年都忙於做專案,沒有太多的時間去整理和總結在專案中用過的技術 個人還是覺得技術需要總結提煉和沉澱的,忙到沒時間去總結提公升其實不 是什麼好事,這次講下狀態機,在戰鬥型別的遊戲中角色有多種不同的狀態,而狀態的切換錯綜複雜,23種設計模式中有一種模式叫做狀態模式,不過 這種模式是把狀態切換條件放到各...