Проектирование цепочки состояний - PullRequest
3 голосов
/ 26 апреля 2010

Я хочу смоделировать нечто вроде конечного автомата. У меня есть последовательность состояний (скажем, от StateA до StateZ). Эта последовательность называется цепочкой и реализована внутри как список. Я добавлю состояния в том порядке, в котором я хочу их запускать.

Моя цель - иметь возможность выполнять последовательность действий на моем компьютере (например, щелчки мышью). (Я знаю, что это было сделано миллион раз).

Итак, состояние определяется как:

  1. boolean Precondition() <- Проверяет, является ли для этого состояния какое-либо условие истинным. Например, если я хочу нажать кнопку «Запись» программы, в этом методе я проверю, запущен ли процесс программы или нет. Если это так, перейдите к следующему состоянию в списке цепочек, в противном случае перейдите к тому состоянию, которое было определено как состояние сбоя (обычно это первое из всех состояний). </li>
  2. IState GetNextState() <- Возвращает следующее состояние для оценки. Если Precondition () был успешным, он должен привести к следующему состоянию в цепочке, в противном случае он должен привести к состоянию отказа. </li>
  3. Run() Просто проверяет Precondition() и устанавливает внутренние данные так, чтобы GetNextState() работал как положено.

Итак, наивный подход к этому будет примерно таким:

Chain chain = new Chain();
//chain.AddState(new State(Precondition, FailState, NextState) <- Method structure
chain.AddState(new State(new WinampIsOpenCondition(), null, new <problem here, I want to referr to a state that still wasn't defined!>);

Большая проблема в том, что я хочу сделать ссылку на государство, которое на данный момент еще не было определено. Я мог бы обойти проблему, используя строки при переходе к состояниям и используя внутреннюю хеш-таблицу, но разве нет более ясной альтернативы?

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

Ответы [ 3 ]

2 голосов
/ 26 апреля 2010

Вы можете сделать одно из следующих действий:

  • Определите nextState как изменяемое поле в вашем классе Sta t e, а затем соедините состояния, используя мутатор; например setNextState. Метод setNextState может быть реализован так, чтобы он вызывался только один раз; последующие вызовы вызовут IllegalStateException.
  • Измените интерфейс State, чтобы просто возвращать, выполнено ли предварительное условие или нет (т. Е. Вернуть boolean), и используйте внешний класс "координатор" для перехода по списку, если выполняется предварительное условие. Другими словами, вы знаете , что следующее состояние находится по индексу i + 1, поэтому нет реальной необходимости для каждого состояния явно знать своего преемника .

Учитывая простоту вашего конечного автомата, я бы предпочел второй подход.

1 голос
/ 26 апреля 2010

Я полностью согласен со вторым пунктом из @Adamski. Нет необходимости в том, чтобы состояния были осведомлены о себе, если только вы не планируете обходить состояния в виде алгоритма графа, в отличие от использования внешнего посредника для управления обходом.

Если вы действительно заинтересованы в том, чтобы состояния можно было фактически выразить в виде дерева (даже если оно в настоящее время полностью линейно), чтобы ответить на ваш вопрос по new <problem here, I want to referr to a state that still wasn't defined!>);

Я бы решил, что для каждого записанного действия я бы настроил любой тип контейнера, например массив, для хранения списка действий. Затем я записал бы текущее действие, но отложил добавление его в контейнер. Когда я записываю 2-е действие, я добавляю это к предыдущему действию, помещаю предыдущее действие в массив и затем сохраняю текущее действие.

Когда вы доберётесь до вызова execute, вы нажмете последнее действие, которого нет в контейнере, вместе с действием, которое определяет конец FSM.

Так что у вас будет что-то вроде этого

public State PreviousAction { get; set; }
public IList<State> States { get; private set }
public void QueueAction(State CurrentAction)
{    
    if(PreviousAction != null)
    {        
        States.Add(new State(PreviousAction, CurrentAction)        
    }

    PreviousAction = CurrentAction;    
}

public void Execute()
{    
    States.Add(new State(PreviousAction, State.Terminator));

    States[0].Execute();    
}
1 голос
/ 26 апреля 2010

Это может быть идеальным кандидатом для Шаблон декоратора . Следующий шаг украшает (оборачивает) текущий шаг. Вы можете построить всю цепочку состояний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...