C#中状态机Stateless初使用
参考文献:.net轻量状态机Stateless
一、Stateless介绍
Stateless是一个轻量级、高性能的状态机库,可用在.net应用程序中实现状态流的变化,能够轻松地帮助我们实现状态转换的逻辑。
状态机:"依照指定的状态流程图,根据当前执行的动作,将当前状态按照预定的条件变更到新的状态 "。状态机有4个要素,即现态、条件、动作、次态。其中,现态和条件是“因”, 动作和次态是“果”。
- 现态 - 是指当前对象的状态
- 条件 - 当一个条件满足时,当前对象会触发一个动作
- 动作 - 条件满足之后,执行的动作
- 次态 - 条件满足之后,当前对象的新状态。次态是相对现态而言的,次态一旦触发,就变成了现态
状态迁移图:用来描述一个特定的对象所有可能的状态,以及由于各种事件的发生而引起的状态之间的转移和变化,也是配置状态机按照何种行径的前提 。
二、Stateless的使用
根据上面的状态迁移图,实现状态流变化的Demo。
1、梳理状态流中每个节点的走向和触发动作
如上图。
2、安装 Stateless 库
我这里安装的是这个。
3、定义状态和触发事件
订单状态
/// <summary> /// 订单状态 /// </summary> internal enum OrderState { /// <summary> /// 创建 /// </summary> OrderCreate=0, /// <summary> /// 关闭 /// </summary> Invalided=1, /// <summary> /// 待支付 /// </summary> PendingSign=2, /// <summary> /// 待发货 /// </summary> PendingSend=3, /// <summary> /// 待收货 /// </summary> PendingReceipt=4, /// <summary> /// 待退款 /// </summary> PendingRefund=5, /// <summary> /// 完成 /// </summary> Completed=6, }
订单状态的触发事件
/// <summary> /// 订单状态触发事件 /// </summary> internal enum OrderTrigger { /// <summary> /// 跳转 /// </summary> Jump=0, /// <summary> /// 取消 /// </summary> Cancel=1, /// <summary> /// 支付 /// </summary> Payment=2, /// <summary> /// 配送 /// </summary> Send=3, /// <summary> /// 签收 /// </summary> Sign=4, /// <summary> /// 退款 /// </summary> Refund=5, }
4、实现状态机
根据上面状态流程配置状态机 Stateless,给每个状态节点配置状态转换规则。
internal class OrderStateMachine
{
private readonly StateMachine<OrderState, OrderTrigger> _stateMachine;
public OrderStateMachine(OrderState orderState)
{
//因为 Stateless的状态可能来自ORM 等外部环境,所以初始化状态机时接收一个来自外界的状态 orderState 作为当前状态
_stateMachine = new StateMachine<OrderState, OrderTrigger>(orderState);
ConfigureStateMachine();
}
/// <summary>
/// 配置流程
/// </summary>
private void ConfigureStateMachine()
{
//订单 => 待支付
_stateMachine.Configure(OrderState.OrderCreate)
.Permit(OrderTrigger.Jump, OrderState.PendingSign)
.Permit(OrderTrigger.Cancel,OrderState.Invalided);
// 待支付 => 代发货/关闭
_stateMachine.Configure(OrderState.PendingSign)
.Permit(OrderTrigger.Payment, OrderState.PendingSend)
.Permit(OrderTrigger.Cancel, OrderState.Invalided);
// 代发货 => 待收货/待退款
_stateMachine.Configure(OrderState.PendingSend)
.Permit(OrderTrigger.Send, OrderState.PendingReceipt)
.Permit(OrderTrigger.Cancel,OrderState.PendingRefund);
// 待退款 => 关闭
_stateMachine.Configure(OrderState.PendingRefund)
.Permit(OrderTrigger.Refund, OrderState.Invalided);
// 待收货 => 完成
_stateMachine.Configure(OrderState.PendingReceipt)
.Permit(OrderTrigger.Sign, OrderState.Completed);
}
/// <summary>
/// 获取当前状态的方法
/// </summary>
public StateMachine<OrderState, OrderTrigger> GetStateMachine()
{
return _stateMachine;
}
}
5、状态机的使用
在使用 Fire() 触发状态变换时,如果当前节点没有配置这个触发事件Trigger,则会抛出一个异常。可以在状态转换前,使用 CanFire() 方法来判断配置流程中是否有这个触发事件Trigger,有才让它触发状态转换,也可以使用Ignore方法,忽视一些触发,当触发了此类触发器时,不会抛出异常,而改为忽略该次触发。
状态机调用器:
internal class StateMachineInvoker
{
private readonly OrderStateMachine _stateMachine;
public StateMachineInvoker(OrderStateMachine stateMachine)
{
_stateMachine = stateMachine;
}
public bool UpdateOrderState(OrderTrigger trigger, out OrderState orderState)
{
bool flag;
var machine = _stateMachine.GetStateMachine();
try
{
Console.WriteLine($"修改前的状态为: {machine.State}");
if (machine.CanFire(trigger))
{
// 状态变换,状态变换至 trigger 态
machine.Fire(trigger);
// 此时状态已经发生了变换,下一个状态成立当前状态
Console.WriteLine($"当前状态为: {machine.State}");
if (machine.State == OrderState.Invalided)
{
flag = false;
}
else
{
flag = true;
}
}
else
{
Console.WriteLine($"与Stateless配置流冲突,不能由 {machine.State} 状态直接变换到 {trigger} 状态。");
flag = false;
}
}
catch (Exception e)
{
Console.WriteLine($"错误: {e.Message}");
flag = false;
}
orderState = machine.State;
return flag;
}
}
2、使用状态机
internal class Program
{
static void Main(string[] args)
{
OrderState orderState = OrderState.OrderCreate;
var b = GetTrigger(ref orderState,out int numTrigger, out OrderState newOrderState);
while (b)
{
b = GetTrigger(ref newOrderState, out numTrigger, out newOrderState);
}
Console.WriteLine("结束");
Console.ReadKey();
}
public static bool GetTrigger(ref OrderState orderState,out int numTrigger, out OrderState newOrderState)
{
while (true)
{
Console.WriteLine($"请输入对订单状态的操作://n Jump: 0、Cancel: 1、Payment:2、Send: 3、Sign: 4、Refund: 5");
numTrigger = Convert.ToInt32(Console.ReadLine());
if (numTrigger >= 0 && numTrigger < 7)
{
break;
}
}
// 初始化状态机
OrderStateMachine stateMachine = new OrderStateMachine(orderState);
// 初始化状态机调用器
StateMachineInvoker stateMachineInvoker = new StateMachineInvoker(stateMachine);
var flag = stateMachineInvoker.UpdateOrderState((OrderTrigger)numTrigger,out newOrderState);
return flag;
}
}
好记性不如烂笔头,在学习的路上留下点痕迹。希望能给大家带来帮助,也期待你的点赞和平路。
若有不足之处,还请斧正。