zoukankan      html  css  js  c++  java
  • .Net轻量状态机Stateless的简单应用

      对于大部分系统中流程的变更,是十分正常的事情,小到一个状态的切换,大到整个系统都是围绕业务流再走,复杂点的有工作流引擎,简单点的几个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 = 0,
    
            /// <summary>
            /// 已创建
            /// </summary>
            [Description("已创建")]
            OrderCreated = 3,
    
            /// <summary>
            /// 待支付
            /// </summary>
            [Description("待支付")]
            OrderPendingPay = 6,
    
            /// <summary>
            /// 待配送
            /// </summary>
            [Description("待配送")]
            OrderPendingSend = 9,
    
            /// <summary>
            /// 待收货
            /// </summary>
            [Description("待收货")]
            OrderPendingSign = 12,
    
            /// <summary>
            /// 待退款
            /// </summary>
            [Description("待退款")]
            OrderPendingRefund = 15,
    
            /// <summary>
            /// 已完成
            /// </summary>
            [Description("已完成")]
            OrderCompleted = 18,
        }
    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,望技术有成后能回来看见自己的脚步
  • 相关阅读:
    循环图片 yi
    给大家一个经典的.net情感故事 yi
    [东邪西毒][程序员版][原版][剧情] yi
    Sqlite 使用笔记 中文显示为乱码 yi
    sql2005安装过程,(不装C盘) yi
    Visual Studio 2010 美女与程序员的爱情网剧全集 yi
    IT行业几大职业病 yi
    标准化操作
    【ActiveMQ Tuning】Serializing to Disk
    我的山寨敏捷四季之春
  • 原文地址:https://www.cnblogs.com/CKExp/p/11767985.html
Copyright © 2011-2022 走看看