【问题标题】:State Pattern C# with previous states具有先前状态的状态模式 C#
【发布时间】:2011-12-14 00:22:24
【问题描述】:

我是 C# 中状态模式实现的新手,您能否提供一些有关如何实现它的信息。

我正在使用状态模式在 C# 中重构状态机。目前我的状态机包含 5 个状态,并且只能在状态中前进或后退,即从状态 1 你需要去状态 2、3 和 4 最终到达状态 5。

我可以继续前进

       mainclass.State = new NextSate();

每次您想前进时都会创建一个新状态,但是,一旦所有这些都已创建和/或您想后退,我将需要进入相同的状态,而不仅仅是一个新状态。我怎样才能做到这一点?有没有更好的简单方法?

【问题讨论】:

  • 只需将每个状态存储在内部堆栈中,然后当您想返回时弹出堆栈并将其分配给当前状态
  • 转换是从状态 1 向后还是从状态 5 向前存在?这些无效转换是否会引发异常,或者您是否希望状态转换形成闭环(即从状态 5 向前带您到状态 1,从状态 1 向后带您到状态 5)?

标签: c# design-patterns fsm state-pattern memento


【解决方案1】:

严格来说,如果您要实现经典的 GoF 状态模式,那么状态子类本身负责了解和执行状态转换。 State 的持有者不负责管理转换,并且该模式的大部分意图是将状态转换行为封装在 State 对象中,从而让客户端委托给它们。我引入了一个工厂,它确保每个 State 子类只有一个实例,以确保在来回移动状态时重复使用相同的实例。

public abstract class State
{
   protected StateFactory _factory;
   protected IStateUser _context;

   public State(StateFactory factory, IStateUser context)
   {
      _factory = factory;
      _context = context;
   }

   protected void TransitionTo<T>(Func<T> creator) where T : State
   {
       State state = _factory.GetOrCreate<T>(creator);
       _context.CurrentState = state;
   }

   public abstract void MoveNext();
   public abstract void MovePrevious();
}

public class State1 : State
{
   public State1(StateFactory factory, IStateUser context)
            : base(factory, context)
   {
   }

   public override void MoveNext()
   {
      TransitionTo<State2>(() => new State2(_factory, _context));
   }

   public override void MovePrevious()
   {
      throw new InvalidOperationException();
   }
}

public class State2 : State
{
   public State2(StateFactory factory, IStateUser context)
            : base(factory, context)
   {
   }

   public override void MoveNext()
   {
      TransitionTo<State3>(() => new State3(_factory, _context)); //State 3 is omitted for brevity
   }

   public override void MovePrevious()
   {
      TransitionTo<State1>(() => new State1(_factory, _context));
   }
}

public interface IStateUser
{
   State CurrentState { get; set; }
}

public class Client : IStateUser
{

   public Client()
   {
      var factory = new StateFactory();
      var first = new State1(factory, this);
      CurrentState = factory.GetOrCreate<State1>(() => first);
   }

   public void MethodThatCausesTransitionToNextState()
   {
      CurrentState.MoveNext();
   }

   public void MethodThatCausesTransitionToPreviousState()
   {
      CurrentState.MovePrevious();
   }

   public State CurrentState
   {
      get;
      set;
   }
}

public class StateFactory
{
    private Dictionary<string, State> _states = new Dictionary<string, State>();

    public State GetOrCreate<T>(Func<T> creator) where T : State
    {
        string typeName = typeof(T).FullName;

        if (_states.ContainsKey(typeName))
            return _states[typeName];

        T state = creator();
        _states.Add(typeName, state);

        return state;
    }
}

【讨论】:

    【解决方案2】:

    使用内部堆栈来维护之前的状态:

    public class MyClass
    {
      private Stack<State> _states;
    
      private State _currentState;
    
      public void GoToNextState()
      {
        // If Not last state then
        _states.Push(_currentState);
        _currentState = new NextState();
      }
    
      public void GoToPrevState()
      {
        // if not the first state
        _currentState = _states.Pop();
       }
    }
    

    如果您想保持前向和后向状态,则创建额外的堆栈:

    public class MyClass
    {
        private readonly Stack<State> _nextStates = new Stack<State>();
        private readonly Stack<State> _prevStates = new Stack<State>();
    
        private State _currentState = new SampleState1();
    
        public State CurrentState { get { return _currentState; } }
    
        public void GoToNextState()
        {
            if (_currentState.NextState == null)
                return;
    
            _prevStates.Push(_currentState);
    
            _currentState = _nextStates.Count > 0 ? _nextStates.Pop() : _currentState.NextState;
        }
    
        public void GoToPrevState()
        {
            // if not the first state
    
            _nextStates.Push(_currentState);
            _currentState = _prevStates.Pop();
        }
    }
    

    【讨论】:

    • 我不确定这是否满足 OP 的要求,因为返回先前的状态会丢弃后续的状态,因此如果您返回两次然后向前两次,您将为一个步骤创建一个新的状态实例应该已经创建了。
    • @Steve Rowbotham 那么史蒂夫你有什么建议?顺便说一句,非常感谢@Mohamed Abed。
    • 是的,但根据原始问题处理下一个新状态的方式,这应该是正确的,因为他没有提到想要保持前进状态
    • 您可以添加另一个堆栈(一个用于下一个状态,一个用于上一个状态),然后当您移动下一个时检查下一个状态堆栈是否为空,然后创建一个新状态,当您返回时弹出上一个状态等等
    • 我的观察是基于 OP 帖子的部分内容,即“一旦所有这些都已创建和/或您想向后退,我将需要进入相同的状态,而不仅仅是新的一个”。所以回到之前的状态似乎只是其中的一部分,至少如最初所说的那样。
    【解决方案3】:

    您有某种状态管理器吗?如果是这样,那一个可以保存状态实例。通过将状态转换知识与状态本身分离,您可以让经理决定转换。管理器将检查请求转换的状态:它确定它是“步骤 1”状态,并返回(或创建)“状态 2”状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-05
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多