Шаблон состояний C # с предыдущими состояниями

Я новичок в реализации шаблона состояний на C #, не могли бы вы предоставить некоторую информацию о том, как вы это реализуете.

Я занимаюсь рефакторингом конечного автомата на C # с использованием шаблона состояний. В настоящее время мой конечный автомат содержит 5 состояний, и через состояния можно двигаться только вперед или назад, т.е. из состояния 1 вам нужно перейти в состояние 2, 3 и 4, чтобы наконец перейти в состояние 5. введите здесь описание изображения

Я могу идти вперед, просто делая

       mainclass.State = new NextSate();

который создает новое состояние каждый раз, когда вы хотите двигаться вперед, однако, как только все они будут созданы и / или вы захотите вернуться назад, мне нужно будет перейти в те же состояния, а не только в новое. Как я могу это сделать? Есть ли лучший способ сделать это проще?


person Manolete    schedule 24.10.2011    source источник
comment
Просто сохраните каждое состояние во внутреннем стеке, а затем, когда вы захотите вернуться, вставьте стек и назначьте его текущему состоянию.   -  person Mohamed Abed    schedule 24.10.2011
comment
Существуют ли переходы назад из состояния 1 или вперед из состояния 5? Являются ли эти недопустимые переходы, которые вызывают исключение, или вы хотите, чтобы переходы состояний формировали замкнутый цикл (т.е. вперед из состояния 5 переводит вас в состояние 1, назад из состояния 1 переводит в состояние 5)?   -  person Steve Rowbotham    schedule 24.10.2011


Ответы (3)


Строго говоря, если вы реализуете классический шаблон состояния GoF, то подклассы состояния сами отвечают за знание и выполнение переходов состояний. Владелец состояния не несет ответственности за управление переходами, и большая часть цели шаблона состоит в том, чтобы инкапсулировать поведение перехода между состояниями в объектах состояния и, таким образом, чтобы клиент мог им делегировать. Я представил Factory, который гарантирует, что существует только один экземпляр каждого подкласса 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;
    }
}
person Steve Rowbotham    schedule 24.10.2011

Используйте внутренний стек для сохранения предыдущих состояний:

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();
    }
}
person Mohamed Abed    schedule 24.10.2011
comment
Я не уверен, что это удовлетворяет требованиям OP, потому что возврат к предыдущим состояниям отбрасывает последующие, поэтому, если вы вернетесь дважды, а затем дважды вперед, вы создадите новый экземпляр состояния для шага, который уже должен был быть создан. - person Steve Rowbotham; 24.10.2011
comment
@ Стив Роуботэм. Что ты посоветуешь, Стив? Кстати, большое спасибо @Mohamed Abed. - person Manolete; 24.10.2011
comment
Да, но это должно быть правдой в соответствии со способом обработки следующих новых состояний в исходных вопросах, поскольку он не упомянул о желании поддерживать прямое состояние - person Mohamed Abed; 24.10.2011
comment
Вы можете добавить еще один стек (один для следующих состояний и один для предыдущих состояний), затем, когда вы переходите в следующий, вы проверяете стек nextstates, если он пуст, затем создаете новое состояние, когда вы возвращаетесь, всплываете предыдущее состояние и т. Д. - person Mohamed Abed; 24.10.2011
comment
Мое наблюдение было основано на той части сообщения OP, в которой говорится, что после того, как все они будут созданы и / или вы захотите вернуться, мне нужно будет перейти в те же состояния, а не только в новое. Так что возвращение к предыдущим состояниям кажется только частью этого, по крайней мере, как было заявлено изначально. - person Steve Rowbotham; 24.10.2011
comment
добавлена ​​еще одна версия, которая будет обрабатывать вперед и назад - person Mohamed Abed; 24.10.2011

У вас есть какой-нибудь госменеджер? Если так, то можно было бы держать государственные инстанции. Отделяя сведения о переходе между состояниями от самих состояний, вы позволяете менеджеру принимать решение о переходе. Менеджер проверит состояние, в котором был запрошен переход: он определяет, что это состояние «шаг 1», и возвращает (или создает) состояние «состояние 2».

person jro    schedule 24.10.2011