为了使用Stateless库来展示其多种特性,我们可以创建一个简单的示例,该示例涉及用户购物流程的不同状态,包括浏览商品、添加到购物车、结算和完成购买。下面是如何使用C#和Stateless库来实现这一流程的示例代码:
状态定义
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
using Stateless;
public class StateLessTest
{
private readonly StateMachine<State, Trigger> _machine;
public StateLessTest()
{
_machine = new StateMachine<State, Trigger>(State.Browsing);
// 设置状态进入和离开时的动作
_machine.Configure(State.Browsing)
.OnEntry(() => Console.WriteLine("开始浏览商品"))
.OnExit(() => Console.WriteLine("浏览商品结束"));
_machine.Configure(State.Cart)
.SubstateOf(State.Browsing) // 继承自Browsing状态
.OnEntry(() => Console.WriteLine("商品已添加到购物车"))
.OnExit(() => Console.WriteLine("离开购物车"));
_machine.Configure(State.CheckingOut)
.OnEntry(() => Console.WriteLine("开始结算"))
.OnExit(() => Console.WriteLine("结算完成"));
_machine.Configure(State.Completed)
.OnEntry(() => Console.WriteLine("购买完成"));
// 定义状态转移
_machine.Configure(State.Browsing)
.Permit(Trigger.AddToCart, State.Cart);
// 收到Trigger.Checkout命令时,条件转移 true则到下一个状态
_machine.Configure(State.Cart)
.PermitIf(Trigger.Checkout, State.CheckingOut, () => CanCheckout())
.Permit(Trigger.ContinueBrowsing, State.Browsing);// 如果是继续购物,则返回购物流程
_machine.Configure(State.CheckingOut)
.Permit(Trigger.CompletePurchase, State.Completed)
.Ignore(Trigger.Checkout);//或略Trigger.Checkout命令
// 添加守护条件,防止在不允许的情况下转移状态
_machine.OnTransitioned(transition =>
{
if (transition.Destination == State.CheckingOut && !CanCheckout())
{
throw new InvalidOperationException("无法结算,购物车为空或存在其他问题");
}
});
}
// 示例方法,用于判断是否可以结算
private bool CanCheckout()
{
Console.WriteLine("结算判断---有商品并且有钱可以结算");
// 这里应该有一些逻辑来判断是否可以结算,比如检查购物车是否为空等
// 这里我们简单返回true作为示例
return true;
}
// 触发状态转移的方法
public void AddToCart()
{
_machine.Fire(Trigger.AddToCart);
}
public void Checkout()
{
_machine.Fire(Trigger.Checkout);
}
public void ContinueBrowsing()
{
_machine.Fire(Trigger.ContinueBrowsing);
}
public void CompletePurchase()
{
_machine.Fire(Trigger.CompletePurchase);
}
// 定义状态和触发事件
public enum State
{
Browsing,
Cart,
CheckingOut,
Completed
}
public enum Trigger
{
AddToCart,
Checkout,
ContinueBrowsing,
CompletePurchase
}
// 示例:查询当前状态
public State GetCurrentState()
{
return _machine.State;
}
// 示例:检查是否可以进行特定转移
public bool CanFire(Trigger trigger)
{
return _machine.CanFire(trigger);
}
}
使用测试
// See https://aka.ms/new-console-template for more information
using static StateLessTest;
Console.WriteLine("Hello, World!");
var shoppingMachine = new StateLessTest();
Console.WriteLine("当前状态: " + shoppingMachine.GetCurrentState());
while (true)
{
var cmd1 = Console.ReadLine();
if (cmd1 == "1")
{
Console.WriteLine("执行命令: AddToCart");
shoppingMachine.AddToCart();
}
else if (cmd1 == "2")
{
Console.WriteLine("执行命令: AddToCart");
shoppingMachine.Checkout();
}
else if (cmd1 == "3")
{
Console.WriteLine("执行命令: CompletePurchase");
shoppingMachine.CompletePurchase();
}
else
{
break;
}
// 查询当前状态
Console.WriteLine("当前状态: " + shoppingMachine.GetCurrentState());
}
// 查询当前状态
Console.WriteLine("当前状态: " + shoppingMachine.GetCurrentState());
// 检查是否可以触发某个事件
Console.WriteLine("是否可以再次添加到购物车: " + shoppingMachine.CanFire(Trigger.AddToCart));
结果显示
一个打电话的状态机实现
// 配置“Connected”状态下的行为
phoneCall.Configure(State.Connected)
// 当状态机进入“Connected”状态时,开始计时器
.OnEntry(t => StartCallTimer())
// 当状态机离开“Connected”状态时,停止计时器
.OnExit(t => StopCallTimer())
// 内部转换:当接收到MuteMicrophone触发器时,执行静音操作
.InternalTransition(Trigger.MuteMicrophone, t => OnMute())
// 内部转换:当接收到UnmuteMicrophone触发器时,执行取消静音操作
.InternalTransition(Trigger.UnmuteMicrophone, t => OnUnmute())
// 泛型内部转换:当接收到_setVolumeTrigger触发器并携带音量参数时,设置音量
.InternalTransition<int>(_setVolumeTrigger, (volume, t) => OnSetVolume(volume))
// 许可转换:当接收到LeftMessage触发器时,转移到“OffHook”状态
.Permit(Trigger.LeftMessage, State.OffHook)
// 许可转换:当接收到PlacedOnHold触发器时,转移到“OnHold”状态
.Permit(Trigger.PlacedOnHold, State.OnHold);
总结
Stateless库中的StateConfiguration<TState, TTrigger>类是用于定义状态机中单个状态配置的核心类,其中TState表示状态类型,TTrigger表示触发器类型。这个类提供了多种方法来描述状态的入口行为、出口行为、内部转换、状态转换以及其他高级配置。下面是StateConfiguration类中一些关键方法的简单说明:
OnEntry(Action entryAction): 定义当状态机进入此状态时执行的操作。entryAction是一个委托,会在状态进入时被调用,TTrigger参数通常是触发这次状态转换的触发器实例,尽管在此委托中可能不会用到。
OnExit(Action exitAction): 定义当状态机离开此状态时执行的操作。exitAction委托在状态退出前执行,同样接收触发离开此状态的触发器实例作为参数。
InternalTransition(TTrigger trigger, Action action): 定义在不改变当前状态的情况下,响应特定触发器trigger执行的操作action。适用于状态内部处理某些事件而不改变状态的情况。
InternalTransition(TTrigger trigger, Action<T, TTrigger> action): 泛型版本的InternalTransition,允许携带一个泛型参数T,这样在内部转换时可以处理额外的数据或上下文信息。
Permit(TTrigger trigger, TState nextState): 设置当接收到特定的trigger时,状态机从当前状态转移到nextState。
PermitIf(TTrigger trigger, TState nextState, Func condition): 类似于Permit,但在转移前会检查一个布尔条件condition,仅当条件为true时才允许转移。
PermitDynamic(TTrigger trigger, Func nextStateSelector): 动态选择下一个状态,nextStateSelector是一个委托,在状态转换时调用以确定目标状态。
Ignore(TTrigger trigger): 指定当状态机处于当前配置的状态时,忽略特定的trigger,不做任何操作也不改变状态。
WithHistoryType(HistoryType historyType): 配置历史类型,决定重新进入带有子状态的状态时,如何恢复到之前的具体子状态。HistoryType可以是None, Shallow, 或 Deep。
SubstateOf(TState superState): 指定当前配置的状态为超级状态superState的子状态,用于构建嵌套状态结构。
Configure(TState subState): 在当前配置的状态内部进一步配置子状态,允许定义嵌套状态的行为。
ConfigureAny(): 配置适用于任何未明确配置状态的行为,常用于定义默认行为。
这些方法共同构成了状态配置的核心,允许开发者详细定义状态机的逻辑和行为。在实际应用中,通过组合使用这些方法,可以构建出复杂且功能丰富的状态机模型。