对于大部分系统中流程的变更,是十分正常的事情,小到一个状态的切换,大到整个系统都是围绕业务流再走,复杂点的有工作流引擎,简单点的几个if/else收工,但是往往有那种,心有余而力不足的,比简单复杂,比复杂简单,最近,对业务流程的变更这一块一直再琢磨,没有找到一些让我豁然开朗的资料,本次只能是讲讲我的设计过程,作为反面教材去对比的,同时借鉴staleless去简化一下日常中的设计。

一、常用的流程变更

  采用状态控制数据操作,通过操作变更状态,通过switch、if/else去区分状态,然后依据状态做出不同的指示,从一个大家十分熟悉,但又及其简单的例子着手,思路更加清晰。

  对于订单的状态,一开始用几种描述的方式固定下来,代码实现中,有人喜欢枚举,有人喜欢字符串,这都无所谓了,罗马路条条。只是这个流程是相对固定的,与一些业务系统中,十几个转折或是几十个流程节点那样,那种貌似大多使用的都是工作流吧,存在变更,还要容易变更。

  先定义个简单的订单类及用到的枚举值,这些信息应该是很熟悉且常见的。

public class Order
{
public Order(long orderId, string orderName, string orderNo, double price)
{
OrderId = orderId;
OrderName = orderName;
OrderNo = orderNo;
Price = price;
CreateDate = DateTime.Now;
InitOrderState();
} /// <summary>
/// 订单Id
/// </summary>
public long OrderId { get; set; } /// <summary>
/// 订单名称
/// </summary>
public string OrderName { get; set; } /// <summary>
/// 订单编号
/// </summary>
public string OrderNo { get; set; } /// <summary>
/// 创建日期
/// </summary>
public DateTime CreateDate { get; set; } /// <summary>
/// 订单价格
/// </summary>
public double Price { get; set; } /// <summary>
/// 订单状态
/// </summary>
public OrderState OrderState { get; private set; } /// <summary>
/// 初始订单状态
/// </summary>
public void InitOrderState()
{
OrderState = OrderState.OrderCreated;
} /// <summary>
/// 设置订单状态
/// </summary>
/// <param name="orderState"></param>
public void SetOrderState(OrderState orderState)
{
OrderState = orderState;
}
}

Order

  对于枚举值而言,我这貌似没太大含义,但是有些场景下可能会用的到,暂时先搞上

/// <summary>
/// 订单状态
/// </summary>
public enum OrderState
{
/// <summary>
/// 无效
/// </summary>
[Description("无效")]
OrderInvalided = , /// <summary>
/// 已创建
/// </summary>
[Description("已创建")]
OrderCreated = , /// <summary>
/// 待支付
/// </summary>
[Description("待支付")]
OrderPendingPay = , /// <summary>
/// 待配送
/// </summary>
[Description("待配送")]
OrderPendingSend = , /// <summary>
/// 待收货
/// </summary>
[Description("待收货")]
OrderPendingSign = , /// <summary>
/// 待退款
/// </summary>
[Description("待退款")]
OrderPendingRefund = , /// <summary>
/// 已完成
/// </summary>
[Description("已完成")]
OrderCompleted = ,
}

OrderState

  对于买卖双方,各自应有各自的页面,但是为了操作简便,将合并在一起,重心放在流程的变更上,利用Razor语法快速突进,直接将后台数据在页面上展示出来。

  

  对于各条数据,有单独的状态,按照之前状态图中的那种方式,依据每一个状态前后的变更操作,设计相应的方法,比如待配送阶段的订单,完成发货配送后,那么相应的订单变成待收货,此时需要一个完成发货的一个方法,如下,简单方式配置下,先对当前要操作的订单做个状态判定,防止某些操作,其次更改订单状态,后直接跳回订单页,方便看到数据状态的变更。

public IActionResult CompleteSend(long id)
{
var order = orderList.Where(o => o.OrderId == id).First();
if (order.OrderState != OrderState.OrderPendingSend)
{
throw new Exception("状态错误");
} order.SetOrderState(OrderState.OrderPendingSign);//待签收 return RedirectToAction("Index");
}

  按照一系列预先规划的操作,挨个编写,最终直接运行后可以看下变更效果。

  

  这种方式下,就是简单,思路清晰,操作起来顺手,通过人肉编排完成任务,但是一旦流程节点增多,甚至出现了交叉变更的情形,代码中简单情形的if/else已经不能满足时,就有点头痛了。

二、采用Stateless简化流程

  在采用stateless简化一下上面这个流程设计,也作为对stateless的一次掌握,看下简化后的流程设计能否带来什么意想不到的事情。stateless采用行为触发方式推动流程进行,比如说a状态要变到b状态,要经过一个行为,不管是买家点击,卖家点击,定时任务,其它行为触发后的联动触发等,都是以行为进行驱动的,

  

  因此,按照刚开始那张图中设计好的一些状态变更操作,封装成行为触发的枚举。

    /// <summary>
/// 针对订单的操作
/// </summary>
public enum OrderTrigger
{
/// <summary>
/// 跳转
/// </summary>
Jump, /// <summary>
/// 取消
/// </summary>
Cancel, /// <summary>
/// 支付
/// </summary>
Payment, /// <summary>
/// 配送
/// </summary>
Send, /// <summary>
/// 签订
/// </summary>
Sign, /// <summary>
/// 退款
/// </summary>
Refund,
}

  按照预先的设计并利用stateless提供的相关方法完成流程的配置,采用集中式的配置方式,并且这部分配置工作,可以再度升级到使用数据库存储,而采用数据库去动态配置。

//流程配置
orderStateMachine.Configure(OrderState.OrderCreated)
.Permit(OrderTrigger.Jump, OrderState.OrderPendingPay)
.Permit(OrderTrigger.Cancel, OrderState.OrderInvalided); orderStateMachine.Configure(OrderState.OrderPendingPay)
.Permit(OrderTrigger.Payment, OrderState.OrderPendingSend)
.Permit(OrderTrigger.Cancel, OrderState.OrderInvalided); orderStateMachine.Configure(OrderState.OrderPendingSend)
.Permit(OrderTrigger.Send, OrderState.OrderPendingSign)
.Permit(OrderTrigger.Cancel, OrderState.OrderPendingRefund); orderStateMachine.Configure(OrderState.OrderPendingSign)
.Permit(OrderTrigger.Sign, OrderState.OrderCompleted); orderStateMachine.Configure(OrderState.OrderPendingRefund)
.Permit(OrderTrigger.Refund, OrderState.OrderInvalided);

  对于原有控制器部分,只需考虑一个事情,便是,订单状态的改变,把对订单的所有行为,对外抽象成一个动作,更新订单状态,而通过参数,来区分不同的行为

public IActionResult UpdateOrderState(long id, OrderTrigger orderTrigger)
{
var order = orderList.Where(o => o.OrderId == id).First(); var orderManager = new OrderManager(order);
orderManager.TriggerOrderState(orderTrigger); return RedirectToAction("Index");
}

  对于页面部分,改动量不是很大,只在于更新订单状态所用到的方法及参数的变更,按照这个设计思路,同样可以得到如下的变更效果。

  

  并且,如果存在一些,行为配置错误,而在配置过程中没有处理,将直接报错提示,这方便了在一些复杂场景下,纯粹依靠人肉把控状态的变更方式。同时,拿到stateless的副产物状态图,到viz.js或是把viz.js集成到系统中,可以看到具体的配置效果。

  

 至此,对于使用状态机去简化常用方式的设计完成了,至于深入的更复杂的方式去研究,比如配置到数据库中,页面上配置,充分发挥这个轻量状态机的能力,还得花个时间去琢磨。

 仓库地址:https://gitee.com/530521314/Partner.TreasureChest/tree/master/StateMachine/src

2019-10-31,望技术有成后能回来看见自己的脚步

.Net轻量状态机Stateless的简单应用的更多相关文章

  1. .Net轻量状态机Stateless

    很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作流有点大财小用感觉,比如订单业务中,订单状态的变更,涉及到的状态量不是很多,即使通过简单的 ...

  2. 一种简单,轻量,灵活的C#对象转Json对象的方案

    简单,是因为只有一个类 轻量,是因为整个类代码只有300行 灵活,是因为扩展方式只需要继承重写某个方法即可 补充:修正无法处理可空值类型的bug 首先我将这个类称之为JsonBuilder,我希望它以 ...

  3. 一种简单,轻量,灵活的C#对象转Json对象的方案(续)

    本文参考资料 一种简单,轻量,灵活的C#对象转Json对象的方案 [源码]Literacy 快速反射读写对象属性,字段 一段废话 之前我已经介绍了这个方案的名称为JsonBuilder,这套方案最大的 ...

  4. 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler定时器

    在C#WINFORM或者是ASP.NET的WEB应用程序中,根据各种定时任务的需求,比如:每天的数据统计,每小时刷新系统缓存等等,这个时候我们得应用到定时器这个东东. .NET Framework有自 ...

  5. Bourbon – 简单轻量的 Sass 混入(Mixins)库

    Bourbon 是一个简单易用的 Sass 混入(Mixin)库,无需配置.该混入包含用于支持所有现代浏览器的 CSS3 属性前缀.前缀需要确保在旧的浏览器支持优雅降级.Bourbon 使用 SCSS ...

  6. 轻量简单好用的C++JSON库CJsonObject

    1. JSON概述 JSON: JavaScript 对象表示法( JavaScript Object Notation) .是一种轻量级的数据交换格式. 它基于ECMAScript的一个子集.许多编 ...

  7. Prezento – 轻量、简单的 jQuery 幻灯片插件

    Prezento 是一个超级简单的 jQuery 幻灯片插件.可以让你网页以新颖的交互方式呈现.另外,Prezento 支持响应式设计,配置项也很灵活,可以根据你需要的效果配置. 您可能感兴趣的相关文 ...

  8. flutter最简单轻量便捷的路由管理方案NavRouter

    大家好,我是CrazyQ1,今天给大家推荐一个路由管理方案,用的非常不错的,叫nav_router. 项目地址是:https://github.com/fluttercandies/nav_route ...

  9. Raspkate - 基于.NET的可运行于树莓派的轻量型Web服务器

    最近在业余时间玩玩树莓派,刚开始的时候在树莓派里写一些基于wiringPi库的C语言程序来控制树莓派的GPIO引脚,从而控制LED发光二极管的闪烁,后来觉得,是不是可以使用HTML5+jQuery等流 ...

随机推荐

  1. Android Studio [RecyclerView/瀑布流显示]

    PuRecyclerViewActivity.java package com.xdw.a122.recyclerview; import android.support.v7.app.AppComp ...

  2. maven 3.6的安装

    呵呵,按网上的步骤不能安装maven3.6,所以降低版本.因为3.6的没有bin文件夹. 下载地址:https://archive.apache.org/dist/maven/maven-3/3.5. ...

  3. Player的跟踪狂 -- Camera

    P.S.很多游戏里的Player都会设置的被跟踪,是人性的扭曲,还是XXX,正在解密. 第三人称视角 camera紧跟player背后(角度随player改变) using System.Collec ...

  4. 2017春季_京东_Java后端研发岗面经

    纸上得来终觉浅,绝知此事要躬行  ——2017春季Java后端研发工程师面试心得 收获offer:上海汉得+北京中科软+成都百词斩+成都诺基亚研发中心+清华大学计算机研究所等offer.阿里一面猝.京 ...

  5. nginx的ngx_http_geoip2模块以精准禁止特定地区IP访问

    要求:对网站的信息,比如某个访问节点不想国内或者国外的用户使用,禁止国内或者国外或者精确到某个城市的那种情况. 解决方式:1.Cloudfalre来实现禁止特定国家的ip访问,比较简单,但是需要mon ...

  6. SUSE CaaS Platform 4 - 使用 Ceph RBD 作为持久存储(动态)

    图1 架构图 图2 各存储插件对动态供给方式的支持状况 1.所有节点安装 # yum install ceph-common 复制 ceph.conf 到 worker 节点上 # scp admin ...

  7. 夯实Java基础系列16:一文读懂Java IO流和常见面试题

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  8. [Note] Visual Studio Team Service 中的项目 转到 Git

    Git-tf是微软发布的一个Git工具集的补充,用来让开发人员使用git命令与TFS交互,当然现在VSTS已经直接支持git了,现在讲讲以前用了VSTS的老项目如何转到git,保留所有的change ...

  9. 52个有效方法(1) - 了解Objective-C语言的起源

    Objective-C语言使用的是"消息结构"而非"函数调用" "消息结构"和"函数调用"之间的区别 "消息结 ...

  10. BUUCTF刷题记录(Web方面)

    WarmUp 首先查看源码,发现有source.php,跟进看看,发现了一堆代码 这个原本是phpmyadmin任意文件包含漏洞,这里面只不过是换汤不换药. 有兴趣的可以看一下之前我做的分析,http ...