在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
介绍什么是状态机和状态模式状态机是一种用来进行对象建模的工具,它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前” 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态, 状态机停止。 状态模式主要用来解决对象状态转换比较复杂的情况。它把状态的逻辑判断转移到不同的类中,可以把复杂的逻辑简单化。 状态机的要素状态机有4个要素,即现态、条件、动作、次态。其中,现态和条件是“因”, 动作和次态是“果”。
Stateless
由于整个项目基于.NET Standard的编写的,所以在.NET Framework和.NET Core项目中都可以使用。 项目源代码 https://github.com/dotnet-state-machine/stateless 以下是一个使用Stateless编写的打电话流程 var phoneCall = new StateMachine<State, Trigger>(State.OffHook); phoneCall.Configure(State.OffHook) .Permit(Trigger.CallDialled, State.Ringing); phoneCall.Configure(State.Ringing) .Permit(Trigger.CallConnected, State.Connected); phoneCall.Configure(State.Connected) .OnEntry(() => StartCallTimer()) .OnExit(() => StopCallTimer()) .Permit(Trigger.LeftMessage, State.OffHook) .Permit(Trigger.PlacedOnHold, State.OnHold); // ... phoneCall.Fire(Trigger.CallDialled); Assert.AreEqual(State.Ringing, phoneCall.State);
Stateless支持的特性
与此同时,还提供一些有用的扩展:
分层状态在以下例子中, phoneCall.Configure(State.OnHold) .SubstateOf(State.Connected) .Permit(Trigger.TakenOffHold, State.Connected) .Permit(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed); 状态的进入和退出事件在前面的例子中, 当电话的状态从已连接(Connected)变为挂起(OnHold)时, 不会触发 外部状态存储有时候,当前对象的状态需要来自于一个ORM对象,或者需要将当前对象的状态保存到一个ORM对象中。为了支持这种外部状态存储, var stateMachine = new StateMachine<State, Trigger>( () => myState.Value, s => myState.Value = s); 内省状态机可以通过 保护子句状态机将根据保护子句在多个转换之间进行选择。 phoneCall.Configure(State.OffHook) .PermitIf(Trigger.CallDialled, State.Ringing, () => IsValidNumber) .PermitIf(Trigger.CallDialled, State.Beeping, () => !IsValidNumber); 注意: 配置中的保护子句必须是互斥的,子状态可以通过重新指定来覆盖状态转换,但是子状态不能覆盖父状态允许的状态转换。 参数化触发器Stateless中支持将强类型参数指定给触发器。 var assignTrigger = stateMachine.SetTriggerParameters<string>(Trigger.Assign); stateMachine.Configure(State.Assigned) .OnEntryFrom(assignTrigger, email => OnAssigned(email)); stateMachine.Fire(assignTrigger, "[email protected]"); 导出DOT图Stateless还提供了一个在运行时生成DOT图代码的功能,使用生成的DOT图代码,我们可以生成可视化的状态机图。 这里我们可以使用 phoneCall.Configure(State.OffHook) .PermitIf(Trigger.CallDialled, State.Ringing, IsValidNumber); string graph = UmlDotGraph.Format(phoneCall.GetInfo()); 生成的DOT图代码例子 digraph { compound=true; node [shape=Mrecord] rankdir="LR" subgraph clusterOpen { label = "Open" Assigned [label="Assigned|exit / Function"]; } Deferred [label="Deferred|entry / Function"]; Closed [label="Closed"]; Open -> Assigned [style="solid", label="Assign / Function"]; Assigned -> Assigned [style="solid", label="Assign"]; Assigned -> Closed [style="solid", label="Close"]; Assigned -> Deferred [style="solid", label="Defer"]; Deferred -> Assigned [style="solid", label="Assign / Function"]; } 图形化之后的DOT图例子 一个BugTracker的例子看完了这么多介绍,下面我们来操练一下, 编写一个Bug的状态机。 假设在当前的BugTracker系统中,Bug有4个种状态Open, Assigned, Deferred, Closed。由此我们可以创建一个枚举类 public enum State { Open, Assigned, Deferred, Closed } 如果想改变Bug的状态,这里有3种动作,Assign, Defer, Close。 public enum Trigger { Assign, Defer, Close } 下面我们列举一下Bug对象可能的状态变化。
如果当前Bug的状态是Deferred, 触发动作Assign, Bug的状态会变为Assigned由此我们可以编写Bug类 public class Bug { State _state = State.Open; StateMachine<State, Trigger> _machine; StateMachine<State, Trigger>.TriggerWithParameters<string> _assignTrigger; string _title; string _assignee; public Bug(string title) { _title = title; _machine = new StateMachine<State, Trigger>(() => _state, s => _state = s); _assignTrigger = _machine.SetTriggerParameters<string>(Trigger.Assign); _machine.Configure(State.Open).Permit(Trigger.Assign, State.Assigned); _machine.Configure(State.Assigned) .OnEntryFrom(_assignTrigger, assignee => _assignee = assignee) .SubstateOf(State.Open) .PermitReentry(Trigger.Assign) .Permit(Trigger.Close, State.Closed) .Permit(Trigger.Defer, State.Deferred); _machine.Configure(State.Deferred) .OnEntry(() => _assignee = null) .Permit(Trigger.Assign, State.Assigned); } public string CurrentState { get { return _machine.State.ToString(); } } public string Title { get { return _title; } } public string Assignee { get { if (string.IsNullOrWhiteSpace(_assignee)) { return "Not Assigned"; } return _assignee; } } public void Assign(string assignee) { _machine.Fire(_assignTrigger, assignee); } public void Defer() { _machine.Fire(Trigger.Defer); } public void Close() { _machine.Fire(Trigger.Close); } }
这里我们先展示一个正常的操作流程。 class Program { static void Main(string[] args) { Bug bug = new Bug("Hello World!"); Console.WriteLine($"Current State: {bug.CurrentState}"); bug.Assign("Lamond Lu"); Console.WriteLine($"Current State: {bug.CurrentState}"); Console.WriteLine($"Current Assignee: {bug.Assignee}"); bug.Defer(); Console.WriteLine($"Current State: {bug.CurrentState}"); Console.WriteLine($"Current Assignee: {bug.Assignee}"); bug.Assign("Lu Nan"); Console.WriteLine($"Current State: {bug.CurrentState}"); Console.WriteLine($"Current Assignee: {bug.Assignee}"); bug.Close(); Console.WriteLine($"Current State: {bug.CurrentState}"); } } 运行结果 下面我们修改代码,我们在创建一个Bug之后,立即尝试关闭它 class Program { static void Main(string[] args) { Bug bug = new Bug("Hello World!"); bug.Close(); } } 重新运行程序之后,程序会抛出以下异常。
当Bug处于Open状态的时候,触发Close动作,由于没有任何次态定义,所以抛出了异常,这与我们前面定义的逻辑相符,如果希望程序支持Open -> Closed的状态变化,我们需要修改Open状态的配置,允许Open状态通过Close动作变为Closed状态。 _machine.Configure(State.Open) .Permit(Trigger.Assign, State.Assigned) .Permit(Trigger.Close, State.Closed); 由此可见我们完全可以根据自身项目的需求,定义一个简单的工作流,Stateless会自动帮我们验证出错误的流程操作。 总结今天我为大家分享了一下.NET中的状态机库Stateless, 使用它我们可以很容易的定义出自己业务需要的状态机,或者基于状态机的工作流,本文大部分的内容都来自官方Github,有兴趣的同学可以深入研究一下。 到此这篇关于.NET中的状态机库Stateless的操作流程的文章就介绍到这了,更多相关.NET状态机Stateless内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论