12

bbv.Common.StateMachineclass 是我见过的最好的状态机代码。但它只缺少一件事:获取当前状态。

这是一个订单跟踪系统:

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为每个州都写会很麻烦,因为我会重复自己!

必须有一个微妙的方法来做到这一点。

4

2 回答 2

17

正如丹尼尔解释的那样,这是设计使然。让我解释一下原因:

状态机允许事件排队。因此,向状态机询问其当前状态可能会产生误导。它当前处于状态 A,但已经有一个事件排队,它将使其进入状态 B。

此外,我认为将状态机内部状态(您在状态机定义中使用的状态)直接与状态机外部状态(您希望保留在数据库中的状态)耦合是一种糟糕的设计。如果您直接将这两者结合起来,您将失去在内部重构状态机而不影响外部(在您的情况下是数据库)的能力。我经常遇到必须将状态 A 拆分为 A1 和 A2 的场景,因为我必须将不同的操作附加到它们,但它们仍然代表环境的相同状态。因此,我强烈建议您将内部和外部状态分开,就像您使用 ExecuteOnEntry() 编写的那样,或者通过提供映射和使用扩展。这是一个可以让你了解当前状态的扩展:

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);

当然,您也可以直接使用此扩展来访问当前状态。为了更简单,让定义状态机的类实现扩展并将自己作为扩展传递。让你摆脱多余的课。

最后一点:当您在https://groups.google.com/forum/?fromgroups#!forum/appccelerate的 google 组中询问有关 bbv.Common(或现在称为 Appccelerate)的问题时,对我来说更容易找到问题并回答它;-)

于 2012-11-13T12:45:32.540 回答
6

这是设计使然。我们将查询状态机的状态视为设计气味。但当然也有例外情况。您有以下两种选择:

  1. 使用ExecuteOnEntry方法来保存订单的状态。这反映了要走的路,因为您不想将状态机的状态泄漏到您的业务逻辑中。
  2. 编写您自己的内部使用的状态机装饰器StateMachine<TState, TEvent>。这暴露了状态。

丹尼尔

于 2012-11-12T20:58:25.193 回答