一个状态机的实现

2019-12-30 16:08:30于丽

话不多说,先看代码:


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
 }