一个状态机的实现
话不多说,先看代码:
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);
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!