無狀態狀態機
一:前言
在專案中經常有一些工單,申請之類需要對狀態進行流轉。這種需求一般都是滿足什麼條件然後就翻轉狀態。這些流程結構相似得邏輯,感覺可以抽象處理。用一個通用得結構處理,可以讓系統更加整潔,程式碼邏輯更加單一。
發現阿里開源一種,輕量級得無狀態狀態機得元件。仔細研究一下,確實很適合這種場景下對程式碼邏輯得解耦,相比於if-else程式碼更加讓人容易理解,也更加優雅。
二:狀態機的模型
- State:狀態
- Event:事件,狀態由事件觸發,引起變化
- Transition:流轉,表示從一個狀態到另一個狀態
- External Transition:外部流轉,兩個不同狀態之間的流轉
- Internal Transition:內部流轉,同一個狀態之間的流轉
- Condition:條件,表示是否允許到達某個狀態
- Action:動作,到達某個狀態之後,可以做什麼
- StateMachine:狀態機
三:怎麼使用
這裡我們假設幾個簡單的邏輯需要處理:
- 我們需要從待稽核的狀態需要流轉到稽核通過的狀態,稽核前需要做一個稽核校驗,稽核通過後需要傳送簡訊通知使用者。
外部狀態流轉實現:
- 定義狀態列舉(待稽核,稽核通過,拒絕)
static enum States {
WAITE, PASS, REJECT
}
- 定義一個事件列舉,稽核事件/稽核拒絕/提交申請/內部狀態流轉
static enum Events {
AUDIT, AUDIT_REJECT, COMMIT, COMPLETE_INFORMATION
}
// 上下文物件
static class Context{
String operator = "flw";
String entityId = "7758258";
}
- 按照上面需求配置一個狀態機
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
builder.externalTransition() // 外部狀態流轉
.from(States.WAITE) // 起始狀態:待稽核
.to(States.PASS) // 目的狀態:稽核通過
.on(Events.AUDIT) // 事件:稽核事件
.when(checkCondition()) // 流轉需要校驗的條件,校驗不通過不會進行doAction
.perform(doAction()); // 執行流轉操作
StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
// 列印狀態機裡面的流程流轉圖譜
stateMachine.showStateMachine();
private Condition<StateMachineMyTest.Context> checkCondition() {
return (ctx) -> {return true;}; // 預設返回true
}
private Action<StateMachineMyTest.States, StateMachineMyTest.Events, StateMachineMyTest.Context> doAction() {
return (from, to, event, ctx)->{
System.out.println(ctx.operator+" is operating "+ctx.entityId+" from:"+from+" to:"+to+" on:"+event);
};
}
執行狀態機的列印圖譜結果:
-----StateMachine:TestStateMachine-------
State:PASS
State:WAITE
Transition:WAITE-[AUDIT, EXTERNAL]->PASS
------------------------
- 執行狀態機
// 通過狀態機執行 待稽核狀態執行稽核操作,
States target = stateMachine.fireEvent(States.WAITE, Events.AUDIT, new Context());
Assert.assertEquals(States.PASS, target); // 成功就會返回目標狀態機,校驗失敗就會返回原來的狀態
執行結果:
flw is operating 7758258 from:WAITE to:PASS on:AUDIT
如果 checkCondition
返回false ,就不會執行doAction
操作。
內部狀態流轉實現
- 假設現在只是使用者補全資料,只需要進行一些更新資料操作,不需要狀態流轉。這種需求可以通過內部狀態流轉實現
@Test
public void testExternalNormal(){
StateMachineBuilder<States, Events, Context> builder = StateMachineBuilderFactory.create();
builder.externalTransition()// 內部流轉
.from(States.WAITE)
.to(States.PASS)
.on(Events.COMPLETE_INFORMATION)
.when(checkCondition())
.perform(doAction());
StateMachine<States, Events, Context> stateMachine = builder.build(MACHINE_ID);
// 列印狀態機裡面的流程流轉圖譜
stateMachine.showStateMachine();
// 通過狀態機執行 待稽核狀態執行稽核操作,
States target = stateMachine.fireEvent(States.WAITE, Events.AUDIT, new Context());
Assert.assertEquals(States.WAITE, target);
}
執行結果:
-----StateMachine:TestStateMachine-------
State:PASS
State:WAITE
Transition:WAITE-[COMPLETE_INFORMATION, EXTERNAL]->PASS
------------------------
對於方法裡面的異常也會丟擲來,也是支援事務操作。
整體的流程圖如下:
四:總結
狀態機非常輕量,支援事務操作,對於狀態流轉比較對的場景邏輯解耦還是比較優雅的。
連結:https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine