好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

一个状态机的实现

话不多说,先看代码:

?

interface IState

  {

   string Name { get ; set ; }

   //后件处理

   IList<IState> Nexts { get ; set ; }

   Func<IState /*this*/ , IState /*next*/ > Selector { get ; set ; }

  

  }

  class State : IState

  {

   public string Name { get ; set ; } = "State" ;

 

   IList<IState> IState.Nexts { get ; set ; } = new List<IState>();

   public Func<IState, IState> Selector { get ; set ; }

  }

状态比较简单,一个Name标识,一个后件状态列表,然后一个状态选择器。

比如状态a,可以转移到状态b,c,d,那么选择器就是其中一个。至于怎么选,就让用户来定义实际的选择器了。

?

delegate bool HandleType<T>(IState current, IState previous, ref T value);

  interface IContext<T> : IEnumerator<T>, IEnumerable<T>

  {

   //data

   T Value { get ; set ; }

   //前件处理

   IDictionary<Tuple<IState /*this*/ , IState /*previous*/ >, HandleType<T>> Handles { get ; set ; }

   IState CurrentState { get ; set ; }

   bool transition(IState next);

  }

和状态类State关注后件状态不同,上下文类Context关注前件状态。当跳转到一个新的状态,这个过程中就要根据当前状态来实施不同的策略。比如想进入状态c,根据当前状态是a, b,d 有不同的处理程序。这种转移处理程序,是一一对应的,所以用了 Tuple<进入的状态,当前状态> 来描述一个跳转链。然后用Dictionary 捆绑相关的处理程序。

上下文会携带 T Value 数据,要怎么处理这种数据?我是通过ref 参数来传递给处理程序。因为我不想IState 关心上下文的构造,它只需要关注实际的数据 T value;

上下文保存数据和当前状态,然后通过transiton 让用户控制状态的转移。这里面有一个重复,因为IState有选择器来控制状态转移了。为什么要这么处理?我是为了构造一个跳转序列。引入IEnumerator和IEnumerable接口,然状态可以在选择器的作用下自动跳转,然后用foreach 读取结果序列(只是不知道有什么用)。

?

class Context<T> : IContext<T>

  {

   T data;

   T IContext<T>.Value { get =>data ; set =>data = value; }

   IDictionary<Tuple<IState, IState>, HandleType<T>> IContext<T>.Handles { get ; set ; }

    = new Dictionary<Tuple<IState, IState>, HandleType<T>>();

   public IState CurrentState { get ; set ;}

   T IEnumerator<T>.Current => ( this as IContext<T>).Value ;

   object IEnumerator.Current => ( this as IContext<T>).Value;

   bool IContext<T>.transition(IState next)

   {

    IContext<T> context= this as IContext<T>;

    if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))

    {

     //前件处理

     var key = Tuple.Create(next, context.CurrentState);

     if (context.Handles.ContainsKey(key) && context.Handles[key] != null )

      if (!context.Handles[key](next, context.CurrentState, ref this .data))

       return false ;

 

     context.CurrentState = next;

     return true ;

    }

    return false ;

   }

   bool IEnumerator.MoveNext()

   {

    //后件处理

    IContext<T> context = this as IContext<T>;

    IState current = context.CurrentState;

    if (current == null )

     throw new Exception( "必须设置初始状态" );

    if (context.CurrentState.Selector != null )

    {

     IState next= context.CurrentState.Selector(context.CurrentState);

     return context.transition(next);

    }

    return false ;

   }

   void IEnumerator.Reset()

   {

    throw new NotImplementedException();

   }

   #region IDisposable Support

   private bool disposedValue = false ; // 要检测冗余调用

   protected virtual void Dispose( bool disposing)

   {

    if (!disposedValue)

    {

     if (disposing)

     {

      // TODO: 释放托管状态(托管对象)。

     }

     // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。

     // TODO: 将大型字段设置为 null。

     disposedValue = true ;

    }

   }

   // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。

   // ~Context() {

   // // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。

   // Dispose(false);

   // }

   // 添加此代码以正确实现可处置模式。

   void IDisposable.Dispose()

   {

    // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。

    Dispose( true );

    // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。

    // GC.SuppressFinalize(this);

   }

   IEnumerator<T> IEnumerable<T>.GetEnumerator()

   {

    return this ;

   }

   IEnumerator IEnumerable.GetEnumerator()

   {

    return this ;

   }

   #endregion

  }

重点关注transition函数和MoveNext函数。

?

bool IContext<T>.transition(IState next)

   {

    IContext<T> context= this as IContext<T>;

    if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))

    {

     //前件处理

     var key = Tuple.Create(next, context.CurrentState);

     if (context.Handles.ContainsKey(key) && context.Handles[key] != null )

      if (!context.Handles[key](next, context.CurrentState, ref this .data))

       return false ;

     context.CurrentState = next;

     return true ;

    }

    return false ;

   }

做的事也很简单,就是调用前件处理程序,处理成功就转移状态,否则退出。

?

bool IEnumerator.MoveNext()

   {

    //后件处理

    IContext<T> context = this as IContext<T>;

    IState current = context.CurrentState;

    if (current == null )

     throw new Exception( "必须设置初始状态" );

    if (context.CurrentState.Selector != null )

    {

     IState next= context.CurrentState.Selector(context.CurrentState);

     return context.transition(next);

    }

    return false ;

   }

MoveNext通过选择器来选择下一个状态。

总的来说,我这个状态机的实现只是一个框架,没有什么功能,但是我感觉是比较容易编写状态转移目录树的。

用户首先要创建一组状态,然后建立目录树结构。我的实现比较粗糙,因为用户要分别构建目录树,前件处理器,还有后件选择器这三个部分。编写测试代码的时候,我写了9个状态的网状结构,结果有点眼花缭乱。要是能统一起来估计会更好一些。

要关注的是第一个状态,和最后的状态的构造,否则无法停机,嵌入死循环。

?

//测试代码

//---------创建部分---------

string mess = "" ; //3  

IState s3 = new State() { Name = "s3" };

//2  

IState s2 = new State() { Name = "s2" };

//1  

IState s1 = new State() { Name = "s1" };

//---------组合起来---------  

s1.Nexts = new List<IState> { s2, s3 };  

s2.Nexts = new List<IState> { s1, s3 };  

s3.Nexts = new List<IState> { }; //注意end写法

//---------上下文---------   

//transition  

IContext< int > cont = new Context< int > { CurrentState=s1}; //begin  

cont.Value = 0;

//---------状态处理器---------

HandleType< int > funcLaji = (IState current, IState previous, ref int v) => { mess += $ "{current.Name}:垃圾{previous.Name}\n" ; v++; return true ; };

//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);

//---------状态选择器---------   

var rval = new Random();  

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 (var stor in cont)

     mess+=$ "状态转变次数:{stor}\n" ;

//直接控制跳转

mess += "\n直接控制状态跳转:\n------------------------\n" ;

cont.transition(s1);

cont.transition(s2);

cont.transition(s3);

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持服务器之家!

原文链接:http://HdhCmsTestcnblogs测试数据/Nobel/p/6361595.html

dy("nrwz");

查看更多关于一个状态机的实现的详细内容...

  阅读:47次