zoukankan      html  css  js  c++  java
  • 我的BO之状态控制

    我的BO
    1-我的BO之强类型
    2-我的BO之数据保护
    3-我的BO之状态控制
    4-我的BO之导航属性

    MIS常有状态

    信息管理系统(MIS)常常有流程,一个流程由多个环节构成,不同的环节的流转通过状态控制。比如简单的购物流程:

    对应着这样的状态:

    结合起来就是状态图:

    状态的控制在MIS中往往起着举足轻重的作用,如先付款才能退款。若没有控制好状态,未付款就能退款,实在荒唐。

    不判断状态的做法

    此做法比较简单,前端根据状态显示不同的界面和操作,提交时后端也不判断状态,直接认为当前肯定处于正确的状态,否则前端也不会提交此请求。这种做法不安全,攻击者可以自行发起请求,绕过界面,不顾当前的状态。本来这么不靠谱的方案不足拿出来说,但是由于种种“现实原因”现实中时而有见此方案。拿出来说是想说明存在这样的方案,但要避免这么干。

    一般的做法

    一般的做法,是在业务逻辑中进行判断。具体来说就是要做具体的业务操作前判断当前的状态,只有当前的状态与预期的一种或多种状态符合时才进行后续的操作。最后更新状态。这种做法,软件写得对不对,全靠人当时的精神状态。从工程学来讲,人是不可靠的。本做法潜在的风险:

    1. 漏了检查状态。业务环节多的情况下,漏掉一小部分是不足为奇的。
    2. 检查了状态,但没有检查全面。有些业务操作可以在N种状态下操作,少判断了一些状态也不难发生。
    3. 判断错了状态。流程稍微复杂,容易发生。
      由于状态控制分散在各个业务中,没有集中管理,导致了以上问题。希望有更好的方案。

    中等复杂的业务流程的状态图:

    推荐的做法

    集中管理全部状态。首先把可能的变化做成状态图(State Diagram/state transition diagram/状态转移图,这几个名是否指同个东西?请懂的人赐教), 凡是状态图没有出现的都是非法。当状态值变化时,在状态图中查找旧值能否直接到达新值。若旧值无法直达新值,说明不是非法调用,就是程序写错了。
    状态图需要转化成数据结构才方便计算机的处理,一般是多个二元组,
    每个二元组是这样的:(旧状态值,新状态值),意思是 旧状态值允许直接变成新状态值。比如简单的购物流程,即转化为这样的结构:

    (开始, 已下单)
    (已下单, 已付款)
    (已付款, 已发货)
    (已发货, 已确认收货)
    (已确认收货, 已评价)
    

    这个状态图我是写死在代码中,也可以存储在任何位置,只要运行时能装载到内存即可。

    无论当前做什么具体的业务操作,只要状态发生变化,就调用检查,从而能防止非法的状态变化。结合前面介绍过的我的BO之数据保护就能全面地防止非法的流程的出现。

    示例代码

    Java

    // 订单
    public class OrderBo extends BoBase {
        Order order;
    
        public Long getId() {
            return order.getId();
        }
    
        protected void setId(Long id) {  /* 每个 set 都不是 public */
            order.setId(id);
            setTrackUpdate();    /* 父类方法,后续文章会介绍 */
        }
    
        // 这里省略若干属性
    
        // 订单状态
        public OrderStatus getStatus() {
            String sStatus = order.getStatus();
            return OrderStatus.valueOf(sStatus);
        }
    
        protected void setStatus(OrderStatus status) {
            OrderStatus.statusChanging(this.getStatus(), status);
            String sStatus = status.toString();
            order.setStatus(sStatus);
            setTrackUpdate();
        }
    
        // 发货
        public void delivery(String expressCompanyName, String  expressNumber) {
            this.setStatus(OrderStatus.已发货);  // 你没有看错,这里可以不判断当前的状态
            this.setExpressCompanyName(expressCompanyName);
            this.setExpressNumber(expressNumber);
            // 其它各种操作
            this.save();    /* 父类方法,后续文章会介绍 */
        }
    }
    
    public enum OrderStatus{
        开始,
        已下单,
        已付款,
        已发货,
        已确认收货,
        已评价;
    
        public static void statusChanging(OrderStatus oldStatus, OrderStatus newStatus) {
            switch (oldStatus) {
                case 开始:
                    switch (newStatus) {
                        case 已下单:
                            return;
                    }
                    break;
                case 已下单:
                    switch (newStatus) {
                        case 已付款:
                            return;
                    }
                    break;
                case 已付款:
                    switch (newStatus) {
                        case 已发货:
                            return;
                    }
                    break;
                case 已发货:
                    switch (newStatus) {
                        case 已确认收货:
                            return;
                    }
                    break;
                case 已确认收货:
                    switch (newStatus) {
                        case 已评价:
                            return;
                    }
                    break;
                case 已评价:
                    break;
            }
            throw CommonException.invalidStatus("订单" + this.getEvaluateStatus() + ",不允许此操作");
        }
    }
    

    未来可能在二元组中加入“动作”,变成三元组:(旧状态值, 动作, 新状态值)。能在状态变化时加上动作一起判断,效果更好。

    感谢 螺丝钉 协助阅稿并提出修改建议。

    系列导航

    1-我的BO之强类型
    2-我的BO之数据保护
    3-我的BO之状态控制
    4-我的BO之导航属性

  • 相关阅读:
    E
    D
    Npp ChangeLog
    c++ 书籍(zz)
    再好的工作是为了更好的生活
    如何将JPG格式的图片转换成PNG格式
    点分治
    团体程序设计天梯赛(CCCC) L3021 神坛 的一些错误做法(目前网上的方法没一个是对的) 和 一些想法
    团体程序设计天梯赛(CCCC) L3019 代码排版 方法与编译原理密切相关,只有一个测试点段错误
    团体程序设计天梯赛(CCCC) L3015 球队“食物链” 状态压缩
  • 原文地址:https://www.cnblogs.com/BillySir/p/10849045.html
Copyright © 2011-2022 走看看