Как получить текущее состояние из класса bbv.Common.StateMachine(теперь Appccelerate.StateMachine)?

bbv.Common.StateMachine класс - это лучший код состояния, который я когда-либо видел. Но ему не хватает только одного: получение текущего состояния.

Это система отслеживания заказов:

fsm = new ActiveStateMachine<States, Events>();

        fsm.In(States.OrderCreated)
            .On(Events.Submitted)
            .Goto(States.WaitingForApproval);
        fsm.In(States.WaitingForApproval)
            .On(Events.Reject)
            .Goto(States.Rejected);
        fsm.In(States.WaitingForApproval)
            .On(Events.Approve)
            .Goto(States.BeingProcessed);
        fsm.In(States.BeingProcessed)
            .On(Events.ProcessFinished)
            .Goto(States.SentByMail);
        fsm.In(States.SentByMail)
            .On(Events.Deliver)
            .Goto(States.Delivered);

        fsm.Initialize(States.OrderCreated);
        fsm.Start();
        fsm.Fire(Events.Submitted);
        // Save this state to database

Вы можете легко увидеть, как это работает.

Но я хочу сохранить состояние заказа в базе данных. Поэтому я смогу показать, в каком состоянии находится заказ.

Мне нужен

fsm.GetCurrentState()
//show this state in the a table

метод. На самом деле есть способ: я могу использовать ExecuteOnEntry и изменять локальное значение для каждой записи состояния. Но будет громоздко писать ExecuteOnEntry для каждого состояния, потому что я буду повторяться!

Должен быть деликатный способ сделать это.

Ответ 1

Как объяснил Даниэль, это по дизайну. Позвольте мне объяснить, почему:

Конечный автомат допускает очередность событий. Поэтому, спрашивая государственную машину о ее текущем состоянии, можно ввести в заблуждение. В настоящее время он находится в состоянии A, но уже есть очередь событий, которая заставит его указывать B.

Кроме того, я считаю, что это плохой дизайн, чтобы связать внутренние состояния конечного автомата (те, которые вы используете в определении вашего конечного автомата) напрямую с внешними состояниями конечного автомата (те, которые вы хотите сохранить в базе данных). Если вы соединяете эти два напрямую, вы теряете возможность реорганизовать конечный автомат внутри себя, не нарушая (в вашем случае базу данных). Я часто сталкиваюсь с сценарием, в котором я должен разделить состояние A на A1 и A2, потому что мне приходится прикладывать к ним разные действия, но тем не менее они все еще представляют одно и то же состояние в среде. Поэтому я настоятельно рекомендую вам разделить внутренние и внешние состояния, как вы написали с помощью OnEntryExecute(), либо путем предоставления сопоставления и использования расширения. Это расширение, которое предоставит вам текущее состояние:

public class CurrentStateExtension : ExtensionBase<State, Event>
{
    public State CurrentState { get; private set; }

    public override void SwitchedState(
        IStateMachineInformation<State, Event> stateMachine, 
        IState<State, Event> oldState, 
        IState<State, Event> newState)
    {
        this.CurrentState = newState.Id;
    }
}

Вы можете добавить расширение в конечный автомат таким образом:

currentStateExtension = new CurrentStateExtension();
machine.AddExtension(currentStateExtension);

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

Последнее примечание: когда вы задаете вопросы о bbv.Common(или Appccelerate, как он называется сейчас) в группе google в https://groups.google.com/forum/?fromgroups#!forum/appccelerate, это проще для мне найти вопрос и ответить на него; -)

Ответ 2

Это по дизайну. Мы рассматриваем вопрос о состоянии состояния машины как дизайнерского запаха. Но, конечно, есть исключения. У вас есть следующие два варианта:

  • Используйте методы ExecuteOnEntry, чтобы сохранить состояние заказа. Это отражает путь, потому что вы не хотите течь состояния statemachine в свою бизнес-логику.
  • Напишите свой собственный декодер государственного аппарата, который использует внутренне StateMachine<TState, TEvent>. Это предоставляет состояние.

Daniel