一个状态机的实现
话不多说,先看代码:
interfaceIState { stringName{get;set;} //后件处理 IList<IState>Nexts{get;set;} Func<IState/*this*/,IState/*next*/>Selector{get;set;} } classState:IState { publicstringName{get;set;}="State"; IList<IState>IState.Nexts{get;set;}=newList<IState>(); publicFunc<IState,IState>Selector{get;set;} }
状态比较简单,一个Name标识,一个后件状态列表,然后一个状态选择器。
比如状态a,可以转移到状态b,c,d,那么选择器就是其中一个。至于怎么选,就让用户来定义实际的选择器了。
delegateboolHandleType<T>(IStatecurrent,IStateprevious,refTvalue); interfaceIContext<T>:IEnumerator<T>,IEnumerable<T> { //data TValue{get;set;} //前件处理 IDictionary<Tuple<IState/*this*/,IState/*previous*/>,HandleType<T>>Handles{get;set;} IStateCurrentState{get;set;} booltransition(IStatenext); }
和状态类State关注后件状态不同,上下文类Context关注前件状态。当跳转到一个新的状态,这个过程中就要根据当前状态来实施不同的策略。比如想进入状态c,根据当前状态是a,b,d有不同的处理程序。这种转移处理程序,是一一对应的,所以用了Tuple<进入的状态,当前状态>来描述一个跳转链。然后用Dictionary捆绑相关的处理程序。
上下文会携带TValue数据,要怎么处理这种数据?我是通过ref参数来传递给处理程序。因为我不想IState关心上下文的构造,它只需要关注实际的数据Tvalue;
上下文保存数据和当前状态,然后通过transiton让用户控制状态的转移。这里面有一个重复,因为IState有选择器来控制状态转移了。为什么要这么处理?我是为了构造一个跳转序列。引入IEnumerator和IEnumerable接口,然状态可以在选择器的作用下自动跳转,然后用foreach读取结果序列(只是不知道有什么用)。
classContext<T>:IContext<T> { Tdata; TIContext<T>.Value{get=>data;set=>data=value;} IDictionary<Tuple<IState,IState>,HandleType<T>>IContext<T>.Handles{get;set;} =newDictionary<Tuple<IState,IState>,HandleType<T>>(); publicIStateCurrentState{get;set;} TIEnumerator<T>.Current=>(thisasIContext<T>).Value; objectIEnumerator.Current=>(thisasIContext<T>).Value; boolIContext<T>.transition(IStatenext) { IContext<T>context=thisasIContext<T>; if(context.CurrentState==null||context.CurrentState.Nexts.Contains(next)) { //前件处理 varkey=Tuple.Create(next,context.CurrentState); if(context.Handles.ContainsKey(key)&&context.Handles[key]!=null) if(!context.Handles[key](next,context.CurrentState,refthis.data)) returnfalse; context.CurrentState=next; returntrue; } returnfalse; } boolIEnumerator.MoveNext() { //后件处理 IContext<T>context=thisasIContext<T>; IStatecurrent=context.CurrentState; if(current==null) thrownewException("必须设置初始状态"); if(context.CurrentState.Selector!=null) { IStatenext=context.CurrentState.Selector(context.CurrentState); returncontext.transition(next); } returnfalse; } voidIEnumerator.Reset() { thrownewNotImplementedException(); } #regionIDisposableSupport privatebooldisposedValue=false;//要检测冗余调用 protectedvirtualvoidDispose(booldisposing) { if(!disposedValue) { if(disposing) { //TODO:释放托管状态(托管对象)。 } //TODO:释放未托管的资源(未托管的对象)并在以下内容中替代终结器。 //TODO:将大型字段设置为null。 disposedValue=true; } } //TODO:仅当以上Dispose(booldisposing)拥有用于释放未托管资源的代码时才替代终结器。 //~Context(){ ////请勿更改此代码。将清理代码放入以上Dispose(booldisposing)中。 //Dispose(false); //} //添加此代码以正确实现可处置模式。 voidIDisposable.Dispose() { //请勿更改此代码。将清理代码放入以上Dispose(booldisposing)中。 Dispose(true); //TODO:如果在以上内容中替代了终结器,则取消注释以下行。 //GC.SuppressFinalize(this); } IEnumerator<T>IEnumerable<T>.GetEnumerator() { returnthis; } IEnumeratorIEnumerable.GetEnumerator() { returnthis; } #endregion }
重点关注transition函数和MoveNext函数。
boolIContext<T>.transition(IStatenext) { IContext<T>context=thisasIContext<T>; if(context.CurrentState==null||context.CurrentState.Nexts.Contains(next)) { //前件处理 varkey=Tuple.Create(next,context.CurrentState); if(context.Handles.ContainsKey(key)&&context.Handles[key]!=null) if(!context.Handles[key](next,context.CurrentState,refthis.data)) returnfalse; context.CurrentState=next; returntrue; } returnfalse; }
做的事也很简单,就是调用前件处理程序,处理成功就转移状态,否则退出。
boolIEnumerator.MoveNext() { //后件处理 IContext<T>context=thisasIContext<T>; IStatecurrent=context.CurrentState; if(current==null) thrownewException("必须设置初始状态"); if(context.CurrentState.Selector!=null) { IStatenext=context.CurrentState.Selector(context.CurrentState); returncontext.transition(next); } returnfalse; }
MoveNext通过选择器来选择下一个状态。
总的来说,我这个状态机的实现只是一个框架,没有什么功能,但是我感觉是比较容易编写状态转移目录树的。
用户首先要创建一组状态,然后建立目录树结构。我的实现比较粗糙,因为用户要分别构建目录树,前件处理器,还有后件选择器这三个部分。编写测试代码的时候,我写了9个状态的网状结构,结果有点眼花缭乱。要是能统一起来估计会更好一些。
要关注的是第一个状态,和最后的状态的构造,否则无法停机,嵌入死循环。
//测试代码 //---------创建部分--------- stringmess="";//3 IStates3=newState(){Name="s3"}; //2 IStates2=newState(){Name="s2"}; //1 IStates1=newState(){Name="s1"}; //---------组合起来--------- s1.Nexts=newList<IState>{s2,s3}; s2.Nexts=newList<IState>{s1,s3}; s3.Nexts=newList<IState>{};//注意end写法 //---------上下文--------- //transition IContext<int>cont=newContext<int>{CurrentState=s1};//begin cont.Value=0; //---------状态处理器--------- HandleType<int>funcLaji=(IStatecurrent,IStateprevious,refintv)=>{mess+=$"{current.Name}:垃圾{previous.Name}\n";v++;returntrue;}; //1 cont.Handles.Add(Tuple.Create(s1,default(IState)),funcLaji); cont.Handles.Add(Tuple.Create(s1,s2),funcLaji); //2 cont.Handles.Add(Tuple.Create(s2,s1),funcLaji); //3 cont.Handles.Add(Tuple.Create(s3,s1),funcLaji); cont.Handles.Add(Tuple.Create(s3,s2),funcLaji); //---------状态选择器--------- varrval=newRandom(); Func<int,int>round=x=>rval.Next(x); s1.Selector=st=>round(2)==0?s2:s3; s2.Selector=st=>round(2)==0?s1:s3;
构造完毕后,就可以使用这个状态机了。
//选择器跳转 mess+="选择器跳转:\n------------------------\n"; foreach(varstorincont) mess+=$"状态转变次数:{stor}\n"; //直接控制跳转 mess+="\n直接控制状态跳转:\n------------------------\n"; cont.transition(s1); cont.transition(s2); cont.transition(s3);
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!