当前位置: 首页 > news >正文

C# 状态机

引言

在业务处理中, 经常需要处理业务对象的状态转换, 比如 bug 状态管理、订单状态管理等, 这类问题可依照状态的复杂度,可以有不同的解决方案。

  1. 简单的顺序状态管理
    如果状态数量很少,同时状态是按照一个方向流转,就可以归到这类。 对应的解决方案很简单, 在业务对象中增加一个表示状态的枚举字段即可。

  2. 复杂的 BPM 状态管理
    这里复杂的状态管理, 典型特征是包含 BPM 的 split/join, 比如合同的会签, 对应的解决方案就使用重量级的工作流引擎, 比如 Java 的 flowable、camunda、 activiti, C#的 Elsa Workflows 等。

  3. 中等复杂的状态管理
    我将不含 BPM 的 split/join 情形的都归到中等复杂程度,最佳解决方案是使用状态机,在每个业务对象中内嵌一个状态机,由状态机复杂状态转换。

对于简单的状态管理,往往后期会逐渐会变得不那么简单,所以, 对于简单的状态管理,我也推荐使用状态机,甚至这么说,只要不涉及到 BPM split/join, 使用状态机都是最佳方案。

状态机的几个概念

  1. 状态转换 transition
    目标状态仅由前置状态经过一个事件触发实现状态转换。

  2. 守卫条件 Guard condition
    从前置状态可以无条件地通过一个触发条件转换到目标状态,也可以有条件地通过触发事件转换。
    这里的条件在状态机中被叫做 guard condition。
    需要说明的是, guard condition 不应该被理解成状态转换的 pre check 条件,而是不同的目标状态的的区分条件。

 (A) ----triggerB (condition1) --> (B1)|| -----triggerB (condition2) --> (B2)

C# Stateless 状态机开源库

Stateless github 是 C#中最流行的状态机实现,功能非常强大:

  • 支持守卫 condition
  • 支持带参数的 trigger
  • 内建很多事件可供绑定
  • 可进行触发检查
  • 支持父子状态

示例代码

···csharp
using System.Runtime.CompilerServices;
using Stateless;

namespace ConsoleApp1
{
internal class Program
{
private static void Main(string[] args)
{
// Console.WriteLine("==================");
// Order goodsOrder = new Order(OrderState.Created);
// goodsOrder.isVirtualOrder = false;
// goodsOrder.pay("OpeatorA");

        // Console.WriteLine("==================");// Order virtualOrder = new Order(OrderState.Created);// virtualOrder.isVirtualOrder = true;// virtualOrder.pay("OpeatorB");// Console.WriteLine("==================");// Order paidOrder = new Order(OrderState.Paid);// paidOrder.ship();// Console.WriteLine("==================");// Order shippedOrder = new Order(OrderState.Shipped);// shippedOrder.ship();//导出 graphviz 流程图(.dot文件)//最好使用绑定状态机最开始的state对象来导出,这样流程图看起来更加漂亮, 当然也可以任意状态的状态机导出。// Console.WriteLine("==================");// Order oneOrder = new Order(OrderState.Created);// Console.WriteLine(oneOrder.exportToGraphviz());//导出 mermaid 流程图//最好使用绑定状态机最开始的state对象来导出,这样流程图看起来更加漂亮, 当然也可以任意状态的状态机导出。Console.WriteLine("==================");Order oneOrder2 = new Order(OrderState.Shipped);Console.WriteLine(oneOrder2.exportToMermaid());}
}public class Order
{public bool isVirtualOrder { get; set; }private string _operatorName;/// <summary>/// 状态机对象应该从属于业务对象/// </summary>private StateMachine<OrderState, OrderTrigger> _stateMachine;/// <summary>/// 声明一个带参数的trigger/// 状态机只允许为一个trigger 枚举值绑定一个带参数的trigger/// </summary>private StateMachine<OrderState, OrderTrigger>.TriggerWithParameters<string> _payTrigger;public Order(OrderState orderState){// 初始化状态机对象// 需要指定状态机的 initialState, 需要注意的是, 它并不是一定是整个流程中最开始的状态, 而是本业务对象的当前状态_stateMachine = new StateMachine<OrderState, OrderTrigger>(orderState);_stateMachine.OnTransitionCompleted((transition) => { Console.WriteLine($"Source:{transition.Source} Destination:{transition.Destination}"); });//非传参trigger,直接使用 trigger enum 即可//传参trigger, 必须声明为 TriggerWithParameters 类型, 参数需要通过 Fire() 函数传入, 需要在“目标State”的Configuration下通过 OnEntryFrom() 来接收参数_payTrigger = _stateMachine.SetTriggerParameters<string>(OrderTrigger.Pay);//演示使用守卫条件guard condition的状态转换,PermitIf()中的条件应该是互斥的.//最好要为守卫条件设置一个描述信息,这个描述信息将会体现到导出流程图的transition label上_stateMachine.Configure(OrderState.Created).PermitIf(OrderTrigger.Pay, OrderState.Paid, () => { return isVirtualOrder == false; }, "Non virtual order").PermitIf(OrderTrigger.Pay, OrderState.Shipped, () => { return isVirtualOrder == true; }, "Virtual order");_stateMachine.Configure(OrderState.Paid)//用来接收Trigger传参的OnEntryFrom()调用, 要放到“目标State”的Configuration下.OnEntryFrom(_payTrigger, operatorName => { _operatorName = operatorName; Console.WriteLine($"{_operatorName}"); }).Permit(OrderTrigger.Ship, OrderState.Shipped);_stateMachine.Configure(OrderState.Shipped).Permit(OrderTrigger.Confirm, OrderState.Closed);}public void pay(string operatorName){Console.WriteLine(_stateMachine.State);_stateMachine.Fire(_payTrigger, operatorName);Console.WriteLine(_stateMachine.State);}public void ship(){if (_stateMachine.CanFire(OrderTrigger.Ship)){Console.WriteLine(_stateMachine.State);_stateMachine.Fire(OrderTrigger.Ship);Console.WriteLine(_stateMachine.State);}else{Console.WriteLine($"Cannot fire {OrderTrigger.Ship} in state {_stateMachine.State}");}}/// <summary>///导出 graphviz 流程图(.dot文件), 可以使用 VS code的  Graphviz Interactive Preview 插件预览.dot 文件流程图///最好使用绑定状态机最开始的state对象来导出,这样流程图看起来更加漂亮, 当然也可以任意状态的状态机导出。/// </summary>/// <returns></returns>public string exportToGraphviz(){return Stateless.Graph.UmlDotGraph.Format(_stateMachine.GetInfo());}/// <summary>/// 将输出内容加到 markdown 的 code block 中, 形式为:/// ```mermaid      流程内容   ```/// VS code 可以安装 Markdown Preview Mermaid Support进行预览/// </summary>/// <returns></returns>public string exportToMermaid(){return Stateless.Graph.MermaidGraph.Format(_stateMachine.GetInfo());}
}public enum OrderState
{ Created, Paid, Shipped, Closed }public enum OrderTrigger
{ Pay, Ship, Confirm }

}
···

输出的graphviz流程图

输出的mermaid流程图

http://www.jsqmd.com/news/31729/

相关文章:

  • PHP 现代特性速查 写出更简洁安全的代码(中篇)
  • 真 CSP 2025 游记
  • [引]Regenerate the SAS key used in HTTP trigger flows
  • AI元人文:大语言模型与价值权衡的共生之道
  • 11月4号
  • AVrecon僵尸网络感染超7万台Linux路由器,潜伏两年终被发现
  • AI元人文:化解算力质疑——降维重构价值计算
  • Gunicorn 基础使用
  • [UNIX] unix classic book
  • [UNIX]A Quarter Century of Unix by Peter H. Salus
  • 2025 年 11 月新风系统厂家推荐排行榜,电竞网咖酒店棋牌室KTV洗浴饭店商场办公室别墅大宅学校诊所中医馆会所美容院,商用家用极寒地区全热交换系统公司推荐!
  • 2025 年 11 月新风系统厂家权威推荐榜:电竞网咖酒店棋牌室KTV洗浴饭店商场办公室别墅大宅学校诊所中医馆会所美容院商用家用全热交换系统精选
  • 2025 年 11 月新风系统厂家推荐排行榜,电竞网咖酒店棋牌室KTV洗浴饭店商场办公室别墅大宅学校诊所中医馆会所美容院,商用家用极寒地区全热交换新风系统公司推荐
  • 2025 年 11 月新风系统厂家推荐排行榜,电竞网咖酒店棋牌室KTV洗浴饭店商场办公室别墅大宅学校诊所中医馆艾灸会所美容院商用家用全热交换极寒地区公司推荐
  • 2025 年 11 月新风系统厂家推荐排行榜,电竞网咖酒店棋牌室KTV洗浴饭店商场办公室别墅大宅学校诊所中医馆会所美容院,商用家用极寒地区全热交换系统公司精选
  • 2025 年 11 月新风系统厂家推荐排行榜,电竞/网咖/酒店/棋牌室/KTV/洗浴/商场/办公室/别墅/学校/诊所/中医馆/会所/美容院/商用/家用/极寒地区/全热交换新风系统公司推荐
  • 2025 年 11 月新风系统厂家推荐排行榜,电竞网咖酒店棋牌室KTV洗浴饭店商场办公室别墅大宅学校诊所中医馆会所美容院,商用家用全热交换极寒地区适用
  • Glide将网络图片压缩成指定大小并保存到本地
  • 认知过程的现象学模型:回到“事情本身”的意识体验
  • AI元人文构想中的“内观照叙事模型”:从心灵哲学到价值计算的桥梁
  • C# DataGridView 大数据量性能优化 - 尼古拉
  • WPF的更新通知
  • [数据仓库] 腾讯数据仓库规范体系 [转]
  • 20251104 之所思 - 人生如梦
  • 怎么设计一个好的Selenium/Appium 自动化框架? 需要考虑哪些问题
  • AIChatManager 应用功能总结
  • [Doris] 度言软件:复杂查询响应速度提升10+倍,基于 Apache Doris 实时数仓建设实践 [转]
  • 第15天(中等题 滑动窗口)
  • Rust-闭包
  • [docker note]