使用状态机的一个好处是可以减少对象可以处于的状态数量。我曾与一个在一个类中有 22 个布尔标志的人一起工作。有很多 if !(something && !somethingElse || !userClicked) …
这类代码难以阅读、难以调试、难以进行单元测试,而且或多或少不可能推断出类的实际状态是什么。22 个布尔标志意味着该类可以在超过 400 万个州中。尝试为此进行单元测试...
状态机可以降低代码的复杂性,但它几乎总是会在新项目开始时使代码变得更加复杂。但是,从长远来看,我发现总体复杂性最终总体上会降低。这是因为它很容易扩展和添加更多状态,因为已经定义的状态可以单独保留。
多年来我发现OOP 和状态机通常是同一方面的两个方面。而且我还发现 OOP 很难,而且很难做到“正确”。
我认为状态机不应该对对象外部可见,包括它的触发器。您很可能希望拥有一个公共的只读状态属性。
我设计类的方式是让调用者不能直接改变状态,或者让调用者直接调用 Fire 方法。相反,我使用作为动作的动词的方法,例如 Validate()。
您的工作流程需要条件,但您可以自由选择放置它们的位置。我建议将业务逻辑与状态机配置分开。我认为这使状态机更易于阅读。
像这样的东西怎么样:
namespace ConsoleApp1
{
using Stateless;
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press Q to stop validating events");
ConsoleKeyInfo c;
do
{
var mpe = new MarketPriceEvent();
mpe.Validate();
c = Console.ReadKey();
} while (c.Key != ConsoleKey.Q);
}
}
public class MarketPriceEvent
{
public void Validate()
{
_machine.Fire(Trigger.Validate);
}
public enum State { Validate, Compare2, ErrorAuditing, Compare1, Storing }
private enum Trigger { Validate, CompareOneOk, CompareTwoOk, Error, }
private readonly StateMachine<State, Trigger> _machine;
public MarketPriceEvent()
{
_machine = new StateMachine<State, Trigger>(State.Validate);
_machine.Configure(State.Validate)
.Permit(Trigger.Validate, State.Compare1);
_machine.Configure(State.Compare1)
.OnEntry(DoEventValidation)
.Permit(Trigger.CompareOneOk, State.Compare2)
.Permit(Trigger.Error, State.ErrorAuditing);
_machine.Configure(State.Compare2)
.OnEntry(DoEventValidationAgainstResource2)
.Permit(Trigger.CompareTwoOk, State.Storing)
.Permit(Trigger.Error, State.ErrorAuditing);
_machine.Configure(State.Storing)
.OnEntry(HandleStoring);
_machine.Configure(State.ErrorAuditing)
.OnEntry(HandleError);
}
private void DoEventValidation()
{
// Business logic goes here
if (isValid())
_machine.Fire(Trigger.CompareOneOk);
else
_machine.Fire(Trigger.Error);
}
private void DoEventValidationAgainstResource2()
{
// Business logic goes here
if (isValid())
_machine.Fire(Trigger.CompareTwoOk);
else
_machine.Fire(Trigger.Error);
}
private bool isValid()
{
// Returns false every five seconds...
return (DateTime.UtcNow.Second % 5) != 0;
}
private void HandleStoring()
{
Console.WriteLine("Awesome, validation OK!");
}
private void HandleError()
{
Console.WriteLine("Oh noes, validation failed!");
}
}
}