StateMachine get starting from 國產的狀態機框架(做者很牛逼!):git
support fluent API and declarative manner to declare a state machine, and enable user to define the action methods in a straightforward manner.github
1.1 StateMachine<T, S, E, C>: T -> class Type of StateMachine S -> class type of State E -> class type of Event C -> class type of Context設計模式
1.2 State Machine Builder 1.2.1 StateMachine = StateMachineBuilder.newStateMachine(initialState); StateMachineBuilder = StateMachineBuilderFactory.create(Self-StateMachine.class, Self-State.class, Self-Event.class, Self-Context.class);安全
1.2.2 狀態機由*TransitionBuilder<能夠構造狀態之間的transition對象> 和 EntryExitActionBuilder<在狀態entry/exit期間的actions> 組成??? 僅僅是由這2個構建而成嗎??????????或者能夠說是收集配置信息 1.2.3 內部狀態在 transtion creation 或者 state action creation 期間 會被明確構建 1.2.4 爲了內存優化, 全部由同一個StateMachineBuilder建立的狀態機實例分享相同的definition data 1.2.5 StateMachineBuilder 以一個懶加載方式建立StateMachine. 當StateMachineBuilder 建立第一個StateMachine實例的時候, 會比較費時間。可是在StateMachine實例建立完之後, 接下來StateMachine實例的建立會比第一次快。 一般, StateMachineBuilder 應該儘量多的被重用。
爲了建立一個StateMachine實例, 用戶首先須要建立StateMachineBuilder.例如: StateMachineBuilder builder = StateMachineBuilderFactory.create(MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class); // create(...) 分別以StateMachine, State, Event, Context類型爲參數,構建StateMachineBuilder.框架
1.3. 流暢的API -> Fluent API 1.3.1 StateMachineBulder被建立後, 用戶能夠利用fluent API 定義狀態機的構成元素: state transition action, for example: I. StateMachineBuilder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.toB).callMethod("fromA2B"); // ExternalTransition = (MyState.A --------MyEvent.toB-----------> MyState.B)[Execute Actions: fromA2B] II. StateMachineBuilder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(MyAction); // 利用StateMachineBuilder建立InternalTransitionBuilder -> 建立最高優先級的InternalTransitionBuilder 而且在MyState.A中觸發MyEvent.WithinA事件, 以後執行MyAction. [NOTE: 在transition complete 後 不會發生狀態的entry/exit] 1.3.2 編寫一個有條件的由事件ToB觸發的從狀態A 到狀態B,而且在transition期間執行一個指定Action: builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.ToB).when( new Condition<MyContext>(){ [@Override](https://my.oschina.net/u/1162528) public boolean isSatisfied(MyContext context){ if(context != null && context.num > 0) return true; return false; } [@Override](https://my.oschina.net/u/1162528) public String name(){ return "MyCondition"; } } ).callMethod("MyActionForThisTransition"); 與上面代碼片斷相等的表示 -> 用whenMvel取代when builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.ToB).whenMvel("MyCondition:::(context!=null && context.num > 0)").callMethod("MyActionForThisTransition"); 1.3.3 進入狀態A的時候執行若干Action: builder.onEntry(MyState.A).perform(Lists.newArrayList(myAction1, myAction2)); 1.4 Method Action Call [Action的調用] 1.4.1 用戶能夠在[transtion || state entry/exit]期間 定義匿名Action, 因爲使用到的Action的地方較多且零散,使得interface Action 的實現方法被硬編碼到不少零散的代碼塊裏, 這樣一來不太好維護, 除此以外, 其餘用戶不能重用零散的Action 實現類。爲了解決上述的這些trouble, squirrel-foundation支持將Action實現類的execute()方法(method call action)定義在StateMachine中, its adventure -> easy to maintain code; can be overrided. The following whill show a code fragment: builder.extenalTransition().from(MyState.A).to(MyState.B).on(MyEvent).callMethod("MyActionCallMethod"); public class MyStateMachine extends AbstractUntypedStateMachine{ public void fromA2B(S from, S to, E event, C context){ System.out.println("__MyAction_Method_Call_For_Transition!"); } } 1.4.2 【'Convention Over Configuration' Recommendation!!!】除了the above描述的以方法名調用Action#execute();外,squirrel-foundation還以[Convention Over Configuration -> 約定優於配置原則]方式支持method call actions. For example: I) protected void transitFromAToBOnToB(MyState from, MyState to, MyEvent event, MyContext context); // 相似約定的這種方法命名transitFrom[SourceStateMame]To[TargetStateName]On[EventName], 而且這種模板方法參數是[MyState, MyState, MyEvent, MyContext] 它將被添加到Actions中。當Transition = (A ---MyEvent----> B)時, 這個action 將會被調用。 II) protected void transitFromAnyToBOnToB(MyState from, MyState to, MyEvent event, MyContext context); // action method 模板: transitFromAnyTo[TargetStateName]On[EventName], 當一個transition 被一個事件觸發從任意狀態到B狀態時 該模板方法將會被調用。 III) protected void exitA(MyState from, MyState to, MyEvent event, MyContext context); // action method 模板:exit[StateName] 當exit A 時, 該模板方法將被調用。相似的還有 entry[StateName], beforeExitAny/afterExitAny , beforeEntryAny/afterEntryAny. 1.4.3 其餘的Action method call模板命名方法: transitFrom[sourceStateName]To[targetStateName]On[EventName]When[ConditionName] ??? ConditionName咋加載?! transitFrom[sourceStateName]To[targetStateName]On[EventName] transitFromAnyTo[targetStateName]On[EventName] transitFrom[sourceStateName]ToAnyOn[EventName] transitFrom[sourceStateName]To[targetStateName] on[EventName] ~ NOTE: 方法模板約定也提供了類AOP的功能, 這提供了以任意粒度提供內置彈性擴展。 對於Action模板方法的應用, 參見"org.squirrelframework.foundation.fsm.ExtensionMethodCallTest". the following shows 等價action 定義: I) builder.transit().fromAny().to("C").on("ToC").callMethod("fromAnyToC"); =>in MyStateMachine中定義模板Action方法: transitFromAnyToCOnToC(MyState from, MyState to, MyEvent event, MyContext context); II) builder.transit().from("B").toAny().on("ToC").callMethod("fromBToAny"); => in MyStateMachine 中定義模板Action方法: transitFromBToAnyOnToC(MyState from, MyState to, MyEvent event, MyContext context); III) builder.transit().from("B").toAny().onAny().callMethod("fromBToAny") => in MyStateMachine定義Action模板方法: transitFromBToAny(MyState from, MyState to, MyEvent event, MyContext context); 上述builder.transit().from(SourceStateName).to(TargetStateName).on(EventName).callMethod(ActionMethodCall);也能夠用註解聲明: @Transitions({ @Transit(from="B", to="E", on="*", callMethod="fromBToEOnAny"), @Transit(from="*", to="E", on="ToE", callMethod="fromAnyToEOnToE")}) [NOTE] 這些Action callMethod 只是附屬到相應的已經存在的transition對象上而不會新建transition對象 1.4.4 也可使用Multiple transition一次性建立多個transtion對象, 例子: I) builder.transitions().from(State.A).toAmoung(State.B, State.C, State.D).onEach(Event.A2B, Event.A2C, Event.A2D).callMethod("a2b|a2c|_"); // ????????????????????? 一次性定義多個transition: transitions(A->B@A2B=>a2b, A->C@A2C=>a2c, A->D@A2D) II) builder.localTransitions().between(State.A).and(State._A).onMutual(Event.A2ANY, Event.ANY2A).perform(Lists.newArrayList(action1, action2)); // ???????????? 1.5 Declarative Annotation (聲明式定義State, Transition,Action 及其餘們之間的轉換和聯繫) 聲明式註解能夠定義在StateMachine實現類或者實現StatemMachine接口的任意接口。註解也能夠混合fluent API使用, 這意味着StateMachine的定義能夠同時支持fluent API 和 Declarative Annotation.???????????? @States({ @State(name="A", entryCallMethod="entryStateA", exitCallMethod="exitStateA"), @State(name="B", entryCallMethod="entryStateB", exitCallMethod="exitStateB") }) @Transitions({ @Transit(from="A", to="B", on="ToB", callMethod="fromAToBOnToB"), @Transit(from="A", to="A", on="WithinA", callMethod="transitWithinA", type=TransitionType.INTERNAL) }) interface MyStateMachine extends StateMachine { // ignore all Action method Call... } 1.6 Converter (類型轉換) // 該Converter主要是爲了在泛型和String之間轉換,主要用於{S -> State, E -> Event}。Squirrel-foundation內置了泛型爲String 或者 枚舉類型的類型轉換器,若是不是String或者枚舉類型, 則須要用戶提供自定義類型轉換器 -> ConverterProvider.register(Converter<T> converter); interface Converter<T> extends SquirrelComponent { String convertToString(T obj); T convertFromString(String name); } 1.7 New State Machine Instance 1.8 Context Insensitive State Machine 有時候狀態轉換不關心context, 這意味着transition最多也就由事件決定。對於這種狀況, 用戶可使用context Insensitive state machine 簡化方法調用參數 -> 聲明context insensitive state machine很簡單, 用戶僅僅須要在StateMachine實現類上增長@ContextInsensitive。這樣,在transition方法參數列表裏就會被忽略 @ContextInsensitive public class ATMStateMachine extends AbstractStateMachine<ATMStateMachine, ATMState, String, Void>{ // no need add context parameter here anymore public transitFromIdleToLoadingOnConnected(ATMState from, ATMState to, String event){ // ...ignore logical implementation } public void entryLoading(ATMState from, ATMState to, String event){ // ...ignore logical implementation } } 1.9 Transition Exception Handling(轉換異常處理) 在狀態轉換器的時候發生異常, 執行的Actions將會被終止而且 State Machine 將會進入StateMachineStatus.ERROR狀態, 這意味着狀態機實例不能再處理任何事件。 若是用戶繼續對發生ERROR的狀態機繼續fire event, 那麼將拋出IllegalStateExceptioin.在轉換階段<包含執行action和調用額外的listener>全部的異常將會被包裝爲TransitionException(未檢查異常)統一個異常類型。當前, 默認的異常處理策略比較簡單的拋異常 protected void afterTransitionCausedException(...) { throw e; } 1.9.1 若是狀態機能夠從異常中回覆, 用戶能夠繼承afterTransitionCausedException(S from, S to, E event, C context); 方法, 而且增長相應的回覆策略。 tip -> 不要忘記把State Machine的狀態(StateMachineStatus)恢復成正常,栗子: @Override protected void afterTransitionCausedException(Object from, Object to, Object event, Object context){ Throwable targetException = getLastException().getTargetException(); if(targetException instanceof IllegalArgumentException && from.equals("A") && to.equals("B") && event.equals("ToB")){ // do some logical handling... // finally set State Machine Status to normal -> StateMachineStatus.IDLE setStatus(StateMachineStatus.IDLE); } else if(...){ } else { super.afterTransitionCausedException(from, to, event, context); } } Advanced Feature 2.0 Define Hierarchical State 層級狀態可能包含嵌套的狀態。 子狀態有可能有子狀態而且這個嵌套能夠處理到任意深度。當一個層級狀態是活躍的, 那麼僅且僅有一個子狀態是活躍的。 這個層級狀態能夠經過API或者annotation被定義. // 定義子狀態以及將父狀態和子狀態相互綁定 void defineSequentialStatesOn(S parentStateId, S... childStateIds); 按照上述添加層級狀態的定義,舉個栗子: builder.defineSequatialStatesOn(A, BinA, CinA); -> A存在BinA和CinA兩個子狀態,第一個定義的子狀態將成爲A狀態的初始狀態 即 BinA是A狀態的initial state.固然層級狀態的定義也可使用註解標識: Annotation: @States({ @State(name="A", entryMethodCall="entryA", exitMethodCall="exitA"), @State(parent="A", name="BinA", entryMethodCall="entryBinA", exitMethodCall="exitBinA", initialState=true), @State(parent="A", name="CinA", entryMethodCall="entryCinA", exitMethodCall="exitCinA") }) 2.1 Define Parallel State(定義並行狀態???) 並行狀態封裝一組子狀態, 當父狀態活躍的時候, 子狀態也同時是活躍的。並行狀態能夠用fluent API 或者註解定義。 API: builder.defineParallelStatesOn(MyState.Root, MyState.RegionState1, MyState.RegionState2); // 設置在RegionState1中子狀態的流轉 builder.defineSequentialStatesOn(MyState.RegionState1, MyState.State11, MyState.State12); builder.extenalTransition().from(MyState.State11).to(MyState.State12).on(MyEvent.Event1); // 設置在RegionState2中的子狀態流轉 builder.defineSequentialStatesOn(MyState.RegionState2, MyState.State21, MyState.State22); bulder.externalTransition().from(MyState.State21).to(MyState.State22).on(MyEvent.Event2); 2.1.1 爲了獲得指定父狀態的子狀態集合, 可使用StateMachine.getSubStatesOn(S parentStateId); 獲得並行狀態集。 當並行狀態集中的狀態都執行完以後, 一個Finish Context event將被觸發???? 這句話莫名其妙的???且看2.2介紹 2.2 Define Context Event(定義Context Event) 納尼??我是誰 我在哪兒 -> 何謂Context Event?! Context event 意味着用戶定義的event已經在狀態機中預約義了context.....squirrel-foundation爲了避免同的場景定義了三種類型的context event! 分別是哪三種呢? -> start terminate finish Start/Terminate Event: 當狀態機Start/Terminate的時候, 會分別使用到getStartEvent()和getTerminateEvent(), 這樣用戶能夠區分調用哪一個action trigger, 舉個栗子: 當狀態機開啓的時候而且進入初始化狀態, 用戶能夠區分調用這些狀態對應的action????? 根據事件肯定transition!而後呢???????????? Finish Event: 當狀態機中的全部並行狀態流轉到final state時, Finish Event將被自動觸發。用戶能夠基於Finish Event定義transition. 定義Context Event 支持2中方式 -> builder API 或者 註解 fluent API: builder.defineFinishEvent(MyState.Finish); builder.defineStartEvent(MyState.Start); builder.defineTerminateEvent(MyState.Terminate); Annotation: @ContextEvent(finish="FINISH") public class MyStateMachine implement StateMachine<MyState, MyState, MyEvent, MyContext>{ // ignore all implementations } 2.3 使用 History States 保存和恢復當前狀態 -> Using History States to Save and Restore the Current State '歷史僞狀態' 容許狀態機記住狀態配置????把歷史狀態做爲目標的transition將會把記錄的配置?????返回給狀態機若是 history type == HistoryType.SHALLOW, 狀態機處理器 -> StateMachineProcessor必須記錄它的父狀態的直系子狀態, 而後再執行退出parent狀態的任何transition。???????? 若是history type == HistoryType.DEEP, 那麼狀態機處理器 -> StateMachineProcessor 必須記錄它的父狀態的全部活躍的子狀態, 而後再執行退出父狀態的任何transition???????????? 使用fluent API 或者 Annotation表示: fluent API : // defined hisotry type of state 'A' as 'deep' 把狀態A 定義爲deep?! 啥是deep? builder.defineSequentialStatesOn(MyState.A, HistoryType.DEEP, MyState.A1, MyState.A2); Annotation: @State(parent="A", name="A1", entryCallMethod="enterA1", exitCallMethod="exitA1", historyType=HistoryType.DEEP) 2.4 Transition Type (轉換類型) 2.4.1 根據UML規範, transition類別介紹: I). Internal Transition Internal Transition 若是被觸發,不會出現entry/exit狀態的狀況 -> 不會引發狀態變動。意味着本次狀態的entry/exit條件不會被調用(固然了, 由於你不會觸發entry/exit時間, 又怎會用到entry/exit的Condition的isSatisfied()). Internal Transition也能夠轉換, 即便 StateMachine處於一個或者多個被嵌套在關聯狀態的Regions中。 II). Local Transition Local Transition 一旦被觸發, 將不會推出複合源狀態(composite source state), 可是它會退出而且從新進入任何在複合狀態中的狀態。 III). External Transition External Transition 一旦被觸發, 將會退出複合源狀態。 2.4.2 經過2種方式定義Transition Type fluent API: builder.externalTransition().from(A).to(B).on(MyEvent.ToB); builder.internalTransition().within(A).on(MyEvent.innerA); builder.localTransition().from(A).to(CinA).on(MyEvent.intoC); Annotation: @Transitions({ @Transit(from="A", to="B", on="ToB"), @Transit(from="A", to="CinA", on="intoC", type=TransitionType.LOCAL), @Transit(from="A", on="innerA", type=TransitioinType.INTERNAL) }) 2.5 事件調度 Event Dispatch 2.5.1 在狀態機的生命週期中,各類事件要被觸發,例如: State Machine Lifecycle Events |--StateMachineEvent /* Base event of all state machine event*/ |--StartEvent /* Fired when state machine started*/ |-- TerminateEvent /* Fired when state machine terminated*/ |--TransitionEvent /* Base event of all Transition event */ |--TransitionStartEvent /* Fired when transition started */ |--TransitionCompleteEvent /* Fired when transition completed */ |--TransitionDeclinedEvent /* Fired when transition declined */ |--TransitionExceptionEvent /* Fired when transition throw exception */ |--TransitionEndEvent /* Fired when transition end no matter declined or completed */ 2.5.2 用戶能夠添加監聽StateMachineEvent的監聽器, 意味着在StateMachine生命週期中全部被觸發的事件將會被這個監聽器監聽 stateMachine.addStateMachineListener(new StateMachineListener(){ @Override public void stateMachineEvent(StateMachineEvent stateMachineEvent){ // add code to handle stateMachineEvent... } }); 而且用戶也能夠經過StateMachine.addTransitionListener(TransitionEvent);監聽TransitionEvent,這意味着在Transition期間發生的事件都會被添加的listener監聽到 -> Transition期間包含:TranstionBeginEvent, TransitionCompleteEvent, TransitionEndEvent. 或者用戶也能夠增長特殊事件的監聽器, 好比, TransitionDeclinedEvent ,當transition 被拒絕的時候。 2.5.3 Declarative Event Listener -> 明式事件監聽器 增長上述所說的事件監聽器看起來很繁瑣, 而且不少通用類型使得代碼很難閱讀。 爲了簡化狀態機的使用, 提供一個非侵入性的集成, squirrel-foundation提供了聲明式的方式來增長listener, 舉個栗子: static class ExternalModule { // 增長transtion end的監聽事件 @OnTransitionEnd @ListenerOrder(10) // 按順序調用listener public void transitionEnd(){ } // 增長transition begin listener @OnTransitionBegin public void transitionBegin(MyEvent event){ } // 增長transition complete listener @OnTransitionComplete public void transitionComplete(String from, String to, MyEvent event, Integer context){ } // 增長transition declined listener @OnTransitionDecline public void transitionDeclined(String from, MyEvent event, Integer context){ } // 增長Action執行以前的listener @OnBeforeActionExecuted public void onBeforeActionExcuted(String sourceState, String targetState, MyEvent event, Integer context, Action<?, ?, ?, ?> action){ } // 增長action 執行以後的listener @OnAfterActionExecuted public void onAfterActionExcuted((String sourceState, String targetState, MyEvent event, Integer context, Action<?, ?, ?, ?> action){ } // 增長執行action的時候拋出異常的監聽器 @OnActionExecException public void onActionExecException(Action<?, ?, ?, ?> action, TransitionException e){ } } // 將定義的listener 添加到StateMachine中 ExternalModule module = new ExternalModule(); stateMachine.addDeclarativeListener(module); // 經過將定義的全部的監聽方法反射,最終 -> StateMachine.addListener(Class<?> eventType, Object listener, Method method); .... stateMachine.removeDeclarativeListener(module); 2.5.4 經過在外部添加listener的方式,不須要實現任何State Machine Listener的接口。僅需在處理監聽邏輯的方法上添加一些特定註解便可。這些方法參數也是類型安全的而且將會自動被推斷去匹配對應的事件。這對於Separation of Concerns(關注點分離???我暫時理解爲一種設計模式吧?!)很好的方式。用戶能夠參考簡單用法: org.squirrelframework.foundation.fsm.StateMachineLogger 2.6 Transition Extension Methods -> transition 生命週期的擴展方法 在2.5中提到了transition整個生命週期中各階段的listener及event, 這裏一樣出現了細分transition的整個生命週期的方法定義,那麼listener中的週期細分方法和AbstractStateMachine中定義的transition細分方法 -> Transition extension methods 有何不一樣?! 區別可從方法命名上看出: AbstractStateMachine#beforeTransitionBegin -> 在TransitionBeginListener以前執行; AbstractStateMachine#afterTransitionCompleted -> 在TransitionCompleteListener以後執行; AbstractStateMachine#afterTransitionEnd -> 在TransitionEndListener以後執行; AbstractStateMachine#afterTransitionDeclined -> 在TransitionDeclinedListener以後執行;AbstractStateMachine#afterTransitionCausedException -> 在TransitionExceptionListener以後執行。 對於Transition Extension Methods, 比較典型的用法是: 能夠在transition期間,利用這些transition extension methods做爲鉤子,回調定義在本身的StateMachine中的這些鉤子方法, 而以前的那些transition event listeners 做爲基於狀態機控制系統的邊界????! 例如, 用戶能夠擴展afterTransitionCausedException用來清理當前上下文資源, 也能夠通知用戶接口module經過TransitionExceptionEvent展現錯誤信息. 2.7 Weight Action -> 權重Action 用戶能夠設置action權重以便調整action的執行順序。在entry/exit state 或者 state transition期間的發生的action會按照action的權重值升序排序。Action 默認的權重值爲0。用戶能夠有2中方式設置權重值。一種是在方法名後面添加權重號, 方法名和權重之間以':'間隔開。舉個栗子: 第一種方式: // 定義entry state的action weight=-1 @State(name="D", entryCallMethod="goEntryD:-1") @Transit(from="A", to="B", on ="ToB", callMethod="goToB:2") 第二種方式: // 重寫Action的weight()方法 Action newAction = new Action(){ @Override public int weight(){ return 10; } } 2.7.1 squirrel-foundation 也支持一種規約的方式定義action weight。action 的命名前綴爲before時該action的weight將會被設置爲100, 一樣命名前綴爲after時, 它的weight將會被設置爲-100。一般這意味着action method名稱前綴爲before時,將首先會被調用,而前綴爲after時, 將最後被調用。"method1:ignore"意味着method1將不會被調用??!更多Weight Action的設置, 請參考test case: 'org.squirrelframework.foundation.fsm.WeightedActionTest' 2.8 Asynchorized Execution -> 異步執行 註解@AsyncExecute能夠寫在method call action或者聲明式的event listener上, 用來標識該action 或者該listener的特定監聽方法能夠異步執行,好比: I) 定義異步調用的action method: @ContextInsensitive @StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class) public class ConcurrentSimpleStateMachine extends AbstractStateMachine { @AsyncExecute protected void fromAToB(String from, String to, String event){ // 這個方法會被異步調用。 // ignore handle logic... } } II) 定義異步調用的event listener: public class DeclarativeListener { @OnTransitionBegin @AsyncExecute public void transitionBegin(String from){ // transition begin listener將會被異步調用處理transition begin event } } 2.8.1 異步執行的任務將會被submit給ExecutorService.用戶能夠經過SquirrelSingletonProvider註冊ExecutorService 實現實例。好比, ExecutorService executorService = Executors.newFixedThreadPool(1); // 定義ExecutorService 實現類 SquirrelSingletonProvider.getInstance().register(ExecutorService.class, executorService); // 註冊ExecutorService實現類。 2.9 State Machine Processor -> 狀態機處理器 爲了在實例化StateMachine時增長後置處理邏輯, 用戶能夠針對state machine的指定類型 註冊後置處理器 -> post processor