squirrel-foundation 状态机入门文档

StateMachine get starting from 国产的状态机框架(做者很牛逼!):git

  1. 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
相关文章
相关标签/搜索