我的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之导航属性

我的BO之状态控制的更多相关文章

  1. 基于 SailingEase WinForm Framework 开发客户端程序(3:实现菜单/工具栏按钮的解耦及状态控制)

    本系列文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...

  2. 线程锁的本质:线程控制、线程状态控制 while if:根据线程的关系(模式)协调线程的执行

    线程锁的本质:线程控制.线程状态控制 while if https://www.cnblogs.com/feng9exe/p/8319000.html https://www.cnblogs.com/ ...

  3. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

  4. Java多线程——线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

  5. filter 实现登录状态控制

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 网站需要做用户登录鉴权控制,没有登录的话,不能访问网站,提示需要登录. 实现方式: 使 ...

  6. RxJava异步请求加载状态控制

    在我看来,RxJava最大的特点就是异步,无论你是解析复杂的数据或是IO操作,我们都可以利用它内置的线程池进行线程间的调度,简单的使用 subscribeOn(Schedulers.io()).doO ...

  7. 11月6日下午PHP注册审核(审核状态控制登录、可以更改审核状态)

    1.创建登录界面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  8. 解密FFmpeg播放状态控制内幕

    上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的 ...

  9. hdu 4634 Swipe Bo bfs+状态压缩

    题目链接 状态压缩记录当前拿到了哪些钥匙, 然后暴力搜索. 搞了好几个小时, 一开始也不知道哪里错了, 最后A了也不知道一开始哪里有问题. #include <iostream> #inc ...

随机推荐

  1. tcpdump命令及输出详解

    一. 使用方法 1. 指定类型 host:指定主机 tcpdump host 192.168.100.1 tcpdump host 192.168.100.1 and !192.168.100.2 t ...

  2. MySQL分布式数据库架构:分库、分表、排序、分页、分组、实现教程

    MySQL分库分表总结: 单库单表 : 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 单库多表 : 随着用户数量的增加, ...

  3. LeetCode:137. 只出现一次的数字 II

    LeetCode:137. 只出现一次的数字 II 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. ...

  4. vue + jenkins 自动部署到指定的目录

    1. 首先选择自由风的构建方式 2. 我的源码在gitlab上,在源码管理下,提供仓库URL和凭证,以及gitlab的分支 3. 在构建环境下选择提供Node &npm bin/folder ...

  5. 容器自动化(一):docker基础(上)

    一,Docker简介,功能特性与应用场景 1.1 Docker简介 Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上 ...

  6. 使用Wireshark对手机抓包设置说明

    一.原因 1.手机目前没有类似的抓包工具可以直接对手机进行抓包 2.一般数据交换的路线是:手机——>运营商——>服务器,可以在手机和运营商中间加一道网卡变成:手机——>PC网卡——& ...

  7. Educational Codeforces Round 72 (Rated for Div. 2)-D. Coloring Edges-拓扑排序

    Educational Codeforces Round 72 (Rated for Div. 2)-D. Coloring Edges-拓扑排序 [Problem Description] ​ 给你 ...

  8. G1垃圾收集器设计目标与改良手段【纯理论】

    在之前已经详细对CMS垃圾回收器进行了学习,今天准备要学习另一个全新的垃圾收集器---G1(Garbage First Collector 垃圾优先的收集器),说是一种全新的,其实G1垃圾收集器已经出 ...

  9. jQuery基础知识1

    jquery的概念 js query jquery库 封装了大量js,封装js的入口函数.兼容性问题.DOM操作.事件.ajax 使用jquery 下载包 引用 <script src=&quo ...

  10. 《奋斗吧!菜鸟》 第七次作业:团队项目设计完善&编码

    项目 内容 这个作业属于哪个课程 任课教师链接 作业要求 https://www.cnblogs.com/nwnu-daizh/p/10980707.html 团队名称 奋斗吧!菜鸟 作业学习目标 团 ...