.Net轻量状态机Stateless
很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作流有点大财小用感觉,比如订单业务中,订单状态的变更,涉及到的状态量不是很多,即使通过简单的if-else也能足够使用,甚至是用上switch去减少if-else的使用,都是可以的,尽管这会丧失某些东西。为更好的优化整个流程,此时会考虑到使用状态模式来解决一些问题。
一、状态模式与状态机
1、状态模式:"允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类 "。 (State Pattern: "Allow an object to alter its behavior when its internal state changes. The object will appear to change its class ".)
2、状态机:"依照指定的状态流程图,根据当前执行的动作,将当前状态按照预定的条件变更到新的状态 "。
//初始化状态机 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.Stat
1、功能特性
2、分层状态
phoneCall.Configure(State.OnHold)
.SubstateOf(State.Connected)
.Permit(Trigger.TakenOffHold, State.Connected)
.Permit(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);
3、状态的进入和退出事件
4、外部状态存储
var stateMachine = new StateMachine<State, Trigger>( () => myState.Value, s => myState.Value = s );
5、内省
public IEnumerable<TTrigger> PermittedTriggers { get { return GetPermittedTriggers(); } } //返回StateMachineInfo对象,包含状态及触发器列表。 _machine.GetInfo();
6、卫语句
phoneCall.Configure(State.OffHook) .PermitIf(Trigger.CallDialled, State.Ringing, () => IsValidNumber) .PermitIf(Trigger.CallDialled, State.Beeping, () => !IsValidNumber)
7、参数化触发器
var assignTrigger = stateMachine.SetTriggerParameters<string>(Trigger.Assign); stateMachine.Configure(State.Assigned) .OnEntryFrom(assignTrigger, email => OnAssigned(email)); stateMachine.Fire(assignTrigger, "joe@example.com");
8、忽视转换和重入状态
phoneCall.Configure(State.Connected)
.Ignore(Trigger.CallDialled);
另外,一个状态能够使用PermitReentry方法配置为重复进入(从本状态到本状态),entry和exit事件也会被再次触发。
stateMachine.Configure(State.Assigned)
.PermitReentry(Trigger.Assigned)
.OnEntry(() => SendEmailToAssignee());
默认情形下,必须明确忽略哪些触发器。 当未配置的触发器被触发时默认是抛出异常,可以通过使用OnUnhandledTrigger配置状态机覆写处理异常情形。
stateMachine.OnUnhandledTrigger((state, trigger) => { });
9、导出DOT格式图
phoneCall.Configure(State.OffHook) .PermitIf(Trigger.CallDialled, State.Ringing, IsValidNumber); string graph = UmlDotGraph.Format(phoneCall.GetInfo());
UmlDotGraph.Format()方法返回代表状态机的字符串,使用DOT graph语言格式。这个可以被支持DOT graph语言的工具渲染。像graphviz.org
10、异步触发
stateMachine.Configure(State.Assigned) .OnEntryAsync(async () => await SendEmailToAssignee()); await stateMachine.FireAsync(Trigger.Assigned);
至此,对于状态机Stateless的功能差不多了解完毕了,开始将状态机融入到项目中实际使用起来,也已经加入到日程中。
2019-09-22,望技术有成后能回来看见自己的脚步