Шаблон состояния C # с предыдущими состояниями - PullRequest
7 голосов
/ 24 октября 2011

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

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

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

       mainclass.State = new NextSate();

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

Ответы [ 3 ]

10 голосов
/ 24 октября 2011

Строго говоря, если вы реализуете классический шаблон состояния GoF, то сами подклассы State отвечают за знание и выполнение переходов между состояниями.Владелец 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;
    }
}
10 голосов
/ 24 октября 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();
    }
}
0 голосов
/ 24 октября 2011

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

...