我的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. React/虚拟DOM

    在说虚拟DOM之前,先来一个引子,从输入url到展现出整个页面都有哪些过程? 1.输入网址 2.DNS解析 3.建立tcp连接 4.客户端发送HTPP请求 5.服务器处理请求 6.服务器响应请求 7. ...

  2. Java 之 OutputStreamReader类

    OutputStreamReader类 1.概述 转换流 java.io.OutputStreamReader ,是Writer的子类,是从字符流到字节流的桥梁. 它使用指定的字符集将字符编码为字节. ...

  3. 在pivotal cloud foundry上申请账号和部署应用

    Created by Wang, Jerry, last modified on Jul 04, 2016 URL: http://run.pivotal.io/ maintain your mobi ...

  4. [#Linux] CentOS 7 美化调优

    优化美化系统,是为了让新系统能更顺眼顺手,符合自己过去在windows下的使用习惯,从而实现平稳过渡. 正如开篇时谈到的,现在的桌面版linux已相当友好(特别是Ubuntu),基本不需要做什么额外设 ...

  5. 【公有云】在阿里云中申请免费ssl证书

    准备 拥有阿里云账号 拥有域名,最好是在同个账号下,方便操作. 申请证书 第一步:进入申请 第二步:选择证书类型 第三步:支付,就是走个流程,不用给钱 第四步:填写证书信息 第五步:验证域名 第六步: ...

  6. postgresql —— 零碎笔记

    聚合函数 -- 聚合查询 SELECT city, max(temp_lo) FROM weather WHERE city LIKE 'S%' GROUP BY city HAVING max(te ...

  7. JavaScript 隐式原型(_proto_)与显示原型(prototype)

    作者:苏墨橘链接:https://www.zhihu.com/question/34183746/answer/59043879来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  8. go常量的定义和枚举类型

    const a,b int = 1,2 const a,b     = 1,2 const ( a = "hello" b,c =3,4 ) 常量数值可作为各种类型使用 枚举类型的 ...

  9. [51Nod 1238] 最小公倍数之和 (恶心杜教筛)

    题目描述 求∑i=1N∑j=1Nlcm(i,j)\sum_{i=1}^N\sum_{j=1}^Nlcm(i,j)i=1∑N​j=1∑N​lcm(i,j) 2<=N<=10102<=N ...

  10. 004_软件安装之_Altium Designer

    文件中有软件简单视频教程,安装有pdf教程 链接:https://pan.baidu.com/s/1ow-OHdsPuAyXCevjCVqEsg 提取码:l2rt 复制这段内容后打开百度网盘手机App ...