前言
有限狀態機(英語:finite-state machine,縮寫:FSM)又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行爲的數學模型。(FROM WIKI)
FSM的核心和作用
狀態機中的核心內容:有限個狀態、通過外部操作引起狀態的轉移。
用來對狀態的流轉進行解耦,使代碼邏輯更加清楚和更容易維護
FSM的組成和分類
組成
- 現態:是指當前所處的狀態。
- 條件:又稱爲事件。當一個條件被滿足,可能將會觸發一個動作,或者執行一次狀態的遷移。
- 動作:條件滿足後執行的動作行爲。動作執行完畢後,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀態。
- 次態:條件滿足後要遷往的新狀態。“次態”是相對於“現態”而言的,“次態”一旦被激活,就轉變成新的“現態”了。
所有的狀態轉換都可以概括爲:F(S, E) -> (A, S’),即如果當前狀態爲S,接收到一個事件E,則執行動作A,同時狀態轉換爲下個狀態S’。
分類
- F(S) -> (A, S’) 型狀態機:下一狀態只由當前狀態決定
- F(S, E) -> (A, S’) 型狀態機:下一狀態不但與當前狀態有關,還與當前輸入值有關
通過映射表實現狀態機
動作接口:
public interface Action {
boolean action(Context context);
}
映射表數據結構:
@Setter
@Getter
public class ActionMapping {
/**
* 當前狀態
*/
private State currentState;
/**
* 次態
*/
private State nextState;
/**
* 動作
*/
private Action action;
/**
* 事件
*/
private Event event;
public static ActionMapping ofMap(State currentState, Event event, State nextState, Action action) {
return new ActionMapping(currentState, event, nextState, action);
}
private ActionMapping(State currentState, Event event, State nextState, Action action) {
this.currentState = currentState;
this.nextState = nextState;
this.action = action;
this.event = event;
}
}
動作中需要使用的上下文參數:
@Getter
@Setter
@AllArgsConstructor
public class Context {
private String param;
}
事件枚舉:
public enum Event {
/**
* 審批通過
*/
APPROVE_PASS,
/**
* 審批拒絕
*/
APPROVE_REFUSED,
/**
* 複覈通過
*/
RECHECK_PASS,
/**
* 複覈不通過
*/
RECHECK_REFUSED;
}
狀態枚舉:
public enum State {
/**
* 待審覈
*/
APPROVE,
/**
* 拒絕
*/
REFUSED,
/**
* 同意
*/
PASS;
}
狀態機:
public class Machine {
/**
* 狀態轉移表
*/
private List<ActionMapping> mappings = new ArrayList<>();
{
//F(APPROVE,APPROVE_PASS)->(PASS,ApprovePassAction)
mappings.add(ActionMapping.ofMap(APPROVE, APPROVE_PASS, PASS, new ApprovePassAction()));
//F(APPROVE,APPROVE_REFUSED)->(REFUSED,ApproveRefusedAction)
mappings.add(ActionMapping.ofMap(APPROVE, APPROVE_REFUSED, REFUSED, new ApproveRefusedAction()));
//F(REFUSED,RECHECK_PASS)->(PASS,RecheckPassAction)
mappings.add(ActionMapping.ofMap(REFUSED, RECHECK_PASS, PASS, new RecheckPassAction()));
//F(REFUSED,RECHECK_REFUSED)->(REFUSED,RecheckRefusedAction)
mappings.add(ActionMapping.ofMap(REFUSED, RECHECK_REFUSED, REFUSED, new RecheckRefusedAction()));
}
public boolean transform(State currentState, Event event, Context context) {
ActionMapping actionMapping = getMapping(currentState, event);
if (null == actionMapping) {
throw new RuntimeException("未找到相應的映射");
}
Action action = actionMapping.getAction();
action.action(context);
return true;
}
private ActionMapping getMapping(State currentState, Event event) {
if (mappings.size() > 0) {
for (ActionMapping n : mappings) {
if (n.getCurrentState().equals(currentState) && n.getEvent().equals(event)) {
return n;
}
}
}
return null;
}
}
審批通過動作的實現:
@Override
public boolean action(Context context) {
System.out.println(MessageFormat.format("審批人{0}審批了價格,審批結果爲通過", context.getParam()));
//TODO 將審批狀態更新爲已審覈通過
return true;
}
}
審批拒絕動作的實現:
public class ApproveRefusedAction implements Action {
@Override
public boolean action(Context context) {
System.out.println(MessageFormat.format("審批人{0}審批了價格,審批結果爲拒絕", context.getParam()));
//TODO 將審批狀態更新爲審覈拒絕
return true;
}
}
複覈通過動作的實現:
public class RecheckPassAction implements Action {
@Override
public boolean action(Context context) {
System.out.println(MessageFormat.format("審批人{0}對未審覈通過的價格進行復核,審批結果爲通過", context.getParam()));
//TODO 將審批狀態更新爲審覈通過
return true;
}
}
複覈拒絕的動作實現
public class RecheckPassAction implements Action {
@Override
public boolean action(Context context) {
System.out.println(MessageFormat.format("審批人{0}對未審覈通過的價格進行復核,審批結果爲通過", context.getParam()));
//TODO 將審批狀態更新爲審覈通過
return true;
}
}
測試類:
public class Test {
public static void main(String[] args) {
Machine machine = new Machine();
machine.transform(APPROVE,APPROVE_REFUSED,new Context("咲夜"));
machine.transform(REFUSED,RECHECK_REFUSED,new Context("咲夜"));
machine.transform(REFUSED,RECHECK_PASS,new Context("帕秋莉"));
}
}
輸出:
squirrel狀態機框架
就像松鼠一樣,小巧,靈活,智能,警覺和可愛的動物,squirrel基礎旨在爲企業使用提供輕量級,高度靈活和可擴展,可診斷,易於使用和類型安全的Java狀態機實現。
squirrel文檔:https://www.yangguo.info/2015/02/01/squirrel/
文檔中很多已經介紹的十分清楚明白了,我這裏就不做過多贅述,只寫一個例子。
事件枚舉:
public enum Event {
/**
* 審批通過
*/
APPROVE_PASS,
/**
* 審批拒絕
*/
APPROVE_REFUSED,
/**
* 複覈通過
*/
RECHECK_PASS,
/**
* 複覈不通過
*/
RECHECK_REFUSED;
}
狀態枚舉:
public enum State {
/**
* 待審覈
*/
APPROVE,
/**
* 拒絕
*/
REFUSED,
/**
* 同意
*/
PASS;
}
上下文參數:
@Getter
@Setter
@AllArgsConstructor
public class Context {
private String param;
}
狀態機
package com.lg.state.squirrel;
import com.lg.state.Context;
import com.lg.state.Event;
import com.lg.state.State;
import org.squirrelframework.foundation.fsm.impl.AbstractStateMachine;
import java.text.MessageFormat;
public class StateMachine extends AbstractStateMachine<StateMachine, State, Event, Context> {
private void approvePassAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("審批人{0}審批了價格,審批結果爲通過", context.getParam()));
//TODO 將審批狀態更新爲已審覈通過
}
private void approveRefusedAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("審批人{0}審批了價格,審批結果爲拒絕", context.getParam()));
//TODO 將審批狀態更新爲審覈拒絕
}
private void recheckPassAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("審批人{0}對未審覈通過的價格進行復核,審批結果爲通過", context.getParam()));
//TODO 將審批狀態更新爲審覈通過
}
private void recheckRefusedAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("審批人{0}對未審覈通過的價格進行復核,審批結果爲不通過", context.getParam()));
//TODO 將審批狀態更新爲審覈不通過
}
}
調用入口示例:
public static void main(String[] args) {
StateMachineBuilder<StateMachine, State, Event, Context> builder =
StateMachineBuilderFactory.create(StateMachine.class, State.class, Event.class, Context.class);
/**
* 狀態轉移表
*/
//F(APPROVE,APPROVE_PASS)->(PASS,approvePassAction)
builder.externalTransition().from(APPROVE).to(PASS).on(APPROVE_PASS).callMethod("approvePassAction");
//F(APPROVE,APPROVE_REFUSED)->(REFUSED,ApproveRefusedAction)
builder.externalTransition().from(APPROVE).to(REFUSED).on(APPROVE_REFUSED).callMethod("approveRefusedAction");
//F(REFUSED,RECHECK_PASS)->(PASS,RecheckPassAction)
builder.externalTransition().from(REFUSED).to(PASS).on(RECHECK_PASS).callMethod("recheckPassAction");
//F(REFUSED,RECHECK_REFUSED)->(REFUSED,RecheckRefusedAction)
builder.externalTransition().from(REFUSED).to(REFUSED).on(RECHECK_REFUSED).callMethod("recheckRefusedAction");
StateMachine machine = builder.newStateMachine(APPROVE);
Context patchouli = new Context("patchouli");
Context xiaoye = new Context("咲夜");
machine.start();
//patchouli審批拒絕
machine.fire(APPROVE_REFUSED,patchouli);
//xiaoye複覈成功
machine.fire(RECHECK_PASS,xiaoye);
}
結果: