狀態機由狀態暫存器和組合邏輯電路構成,能夠根據控制訊號按照預先設定的狀態進行狀態轉移,是協調相關訊號動作、完成特定操作的控制中心。以上是百度百科對狀態機的解釋。
在百科的解釋中,我們可以提煉出狀態機的幾個要素:儲存狀態,邏輯電路,預先設定的狀態轉移路徑,外部來的訊號,內建的特定操作等。由這些關鍵要素我們可以推斷出以下幾點:
-
狀態資料有預先設定的多種值
-
邏輯電路是完成內建特定操作的基礎“程式碼“(基礎設施),由工程司開發
-
狀態能在多種值之間轉移
-
每次轉移都由外部訊號觸發
-
狀態轉移觸發後,會有相應的內建操作
由以上這些特性共同構成了一個獨立的控制中心,而且這個控制中心與外部各種訊號是低耦合的,所有外部訊號都要接入一個共同的控制中心,最終也由控制中心完成訊號的後續流程。
在java開發中,狀態機FSM(有限狀態機)是一種常見的設計模式,常常用於一些複雜的業務場景,解決繁瑣雜亂的if...else...程式碼段。
“Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.“
-物件內部狀態不同會有不同的行為。似乎好像是不同的類一樣。
以電梯為例,電梯有4種狀態:開門、關門、執行、停止。電梯的動作:開門、關門、執行(上升或下降)、停止(懸停不動)。
Col1 | 開門行為 | 關門行為 | 執行行為 | 停止行為 |
---|---|---|---|---|
開門 態 | ❌ | ✔ | ❌ | ❌ |
關門 態 | ✔ | ❌ | ✔ | ✔ |
執行 態 | ❌ | ❌ | ❌ | ✔ |
停止 態 | ✔ | ❌ | ✔ | ❌ |
-
開門狀態下,電梯只有關門行為
-
關門狀態下,除了關門行為其他行為都有
-
執行狀態下,只有停止行為
-
停止狀態下,有開門行為和執行行為
狀態圖如下:
狀態機設計模式的好處:
-
降低程式的複雜度;
-
提高程式的可維護性;
-
狀態機模式體現了開閉原則和單一職責原則。
-
每個狀態都是一個子類,增加狀態就要增加子類;修改狀態只要修改一個類就行了。
這種設計模式將每個狀態的變更後的處理邏輯都做了統一封裝,跟業務程式碼耦合,只接收相互約定的訊號(事件)。
由狀態機的幾個要素我們也可以知道,在一個複雜的業務流程過程中,有多種資料狀態,多種處理動作,以及多種維度的角色。在這樣複雜業務場景下,如果只是簡單使用if...else...語句,首先可讀性就非常差,而且維護起來非常困難,甚至是開發人員都沒辦法理清楚自己寫的if..else語句。如果複雜的業務流程有了變更和新增,那這個維護起來就是個災難,各種複雜問題不得而知。就像下圖一樣
用if..else..程式碼來開發複雜的業務流程,會面臨下面幾個問題:
-
複雜的業務流程,if.else程式碼幾乎無法維護
-
隨著業務的發展,業務過程也需要變更及擴充套件,但if.else程式碼段已經無法支援
-
沒有可讀性,變更風險特別大,可能會牽一髮而動全身,給服務帶來S級風險。
-
其他業務邏輯可能也會跟if.else程式碼塊耦合在一起,帶來更多的問題。
這時候,狀態機就是個非常好的解決方案,能有效地解決這些問題。
一個狀態機定義以後,在某個狀態下就只接收固定的Event,也就是執行指定的操作,這樣流程就能按照預期定義的那樣流轉,不會出現亂入的情況,執行了一些在某狀態下不允許執行的操作,也就是說,狀態的流轉都是在控制範圍,不會超出預設的狀態空間。
-
狀態機建立的控制中心是跟外界低耦合的,通過event通訊。
-
控制中心所有的狀態都是預設好的,不會超預料。
-
狀態的跳轉都是有設定控制條件的,會按照預設的轉移路徑運動。
-
狀態機還非常容易的擴充套件和變更,支援因業務的發展而變更或擴充套件複雜業務流程
狀態機典型的應用有工作流引擎,遊戲中人物狀態變化引擎,訂單交易等。前兩種非常複雜,下面我就簡單以訂單交易這個場景來聊聊狀態機的應用場景以及基於spring statemachine的專案。
配置pom依賴:
<dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>2.1.2.RELEASE</version> </dependency>
狀態列舉和事件列舉:
/**
* @author Batman create on 2019-05-07 14:59
* 狀態列舉類
*/
public enum States {
/*
待支付
*/
UNPAID,
/*
待收貨
*/
WAITING_FOR_RECEIVE,
/*
結束
*/
DONE,
/*
退貨中
*/
WAITING_FOR_GOODSBACK
}
/**
* @author Batman create on 2019-05-07 15:00
*/
public enum Events {
/*
支付
*/
PAY,
/*
收貨
*/
RECEIVE,
/*
退貨
*/
GOODS_REJECTED,
/*
退款
*/
REFUND
}
歡迎加入技術群:獲得更多java,springboot,redis,kafka圈的好友,共圖技術未來
還要配置狀態轉移資訊以及事件處理器邏輯的開發。完整的demo專案,請關注公眾號"前沿科技bot"併傳送"狀態機"獲取。