我的BO之状态控制
我的BO
1-我的BO之强类型
2-我的BO之数据保护
3-我的BO之状态控制
4-我的BO之导航属性
MIS常有状态
信息管理系统(MIS)常常有流程,一个流程由多个环节构成,不同的环节的流转通过状态控制。比如简单的购物流程:
对应着这样的状态:
结合起来就是状态图:
状态的控制在MIS中往往起着举足轻重的作用,如先付款才能退款。若没有控制好状态,未付款就能退款,实在荒唐。
不判断状态的做法
此做法比较简单,前端根据状态显示不同的界面和操作,提交时后端也不判断状态,直接认为当前肯定处于正确的状态,否则前端也不会提交此请求。这种做法不安全,攻击者可以自行发起请求,绕过界面,不顾当前的状态。本来这么不靠谱的方案不足拿出来说,但是由于种种“现实原因”现实中时而有见此方案。拿出来说是想说明存在这样的方案,但要避免这么干。
一般的做法
一般的做法,是在业务逻辑中进行判断。具体来说就是要做具体的业务操作前判断当前的状态,只有当前的状态与预期的一种或多种状态符合时才进行后续的操作。最后更新状态。这种做法,软件写得对不对,全靠人当时的精神状态。从工程学来讲,人是不可靠的。本做法潜在的风险:
- 漏了检查状态。业务环节多的情况下,漏掉一小部分是不足为奇的。
- 检查了状态,但没有检查全面。有些业务操作可以在N种状态下操作,少判断了一些状态也不难发生。
- 判断错了状态。流程稍微复杂,容易发生。
由于状态控制分散在各个业务中,没有集中管理,导致了以上问题。希望有更好的方案。
中等复杂的业务流程的状态图:
推荐的做法
集中管理全部状态。首先把可能的变化做成状态图(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之状态控制的更多相关文章
- 基于 SailingEase WinForm Framework 开发客户端程序(3:实现菜单/工具栏按钮的解耦及状态控制)
本系列文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以 SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...
- 线程锁的本质:线程控制、线程状态控制 while if:根据线程的关系(模式)协调线程的执行
线程锁的本质:线程控制.线程状态控制 while if https://www.cnblogs.com/feng9exe/p/8319000.html https://www.cnblogs.com/ ...
- Java多线程 2 线程的生命周期和状态控制
一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...
- Java多线程——线程的生命周期和状态控制
一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...
- filter 实现登录状态控制
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 网站需要做用户登录鉴权控制,没有登录的话,不能访问网站,提示需要登录. 实现方式: 使 ...
- RxJava异步请求加载状态控制
在我看来,RxJava最大的特点就是异步,无论你是解析复杂的数据或是IO操作,我们都可以利用它内置的线程池进行线程间的调度,简单的使用 subscribeOn(Schedulers.io()).doO ...
- 11月6日下午PHP注册审核(审核状态控制登录、可以更改审核状态)
1.创建登录界面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...
- 解密FFmpeg播放状态控制内幕
上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的 ...
- hdu 4634 Swipe Bo bfs+状态压缩
题目链接 状态压缩记录当前拿到了哪些钥匙, 然后暴力搜索. 搞了好几个小时, 一开始也不知道哪里错了, 最后A了也不知道一开始哪里有问题. #include <iostream> #inc ...
随机推荐
- 快开宝PDA开单器出入库扫码:让批发零售变得更简单
快开宝PDA开单器出现前 批发商户是这样开单和管理的 ★员工痛苦:需要记客户.价格.库存等等,应对报错价.错漏单.盘错货等各种状况. ★老板麻烦:每天要守店.对单.核账,经常因错漏单.库存乱.积压货. ...
- jqTransform:呈现更美的表单
jqTransform,是DFC Engineering写的一个jQuery的样式插件,用于美化表单元素,使用方便简单,能美化所有表单元素包括input,radio,textarea,select,c ...
- SpringBoot properties和yml的区别
一.先附一个yml文件的解析步骤 1.Maven依赖 <dependency> <groupId>org.yaml</groupId> <artifactId ...
- 服务发现之consul理论整理_结合Docker+nginx+Tomcat简单部署案例
目录 一.理论概述 服务发现的概念简述 consul简述 二.部署docker+consul+Nginx案例 环境 部署 三.测试 四.总结 一.理论概述 服务发现的概念简述 在以前使用的是,N台机器 ...
- IP地址简介及Linux网络管理工具
IP地址简介 IP地址又叫网络地址也称逻辑地址,由32位2进制数组成,分4段每段8位,由10进制数表示,范围0~,段与段之间用点隔开采用点分十进制的表示法,在一个网络中ip地址是唯一的,IP地址最主要 ...
- Python_while语句
1.while语句: count = 0 while (count<4): count +=1 print('准备放入第'+str(count)+'个杯子') 注:还是要注意一下,条件之后加“: ...
- 理解serverless无服务
理解serverless无服务 阅读目录 一:什么是serverless无服务? 二:与传统模式架构区别? 三:serverless优缺点? 四:使用serverless的应用场景有哪些? 回到顶部 ...
- Codeforces Round #582 (Div. 3)-G. Path Queries-并查集
Codeforces Round #582 (Div. 3)-G. Path Queries-并查集 [Problem Description] 给你一棵树,求有多少条简单路径\((u,v)\),满足 ...
- Oracle使用游标查询所有数据表备注
功能作用:应用对应的SQL语句,能方便快速的查询Oracle数据库指定用户的所有用户表说明,快速知道每个数据表是做什么的,方便写文档和方案. 运行环境:搭建好Oracle数据库,并使用PQ/SQL D ...
- Java8新特性--日期和时间API
如何正确处理时间 现实生活的世界里,时间是不断向前的,如果向前追溯时间的起点,可能是宇宙出生时,又或是是宇宙出现之前, 但肯定是我们目前无法找到的,我们不知道现在距离时间原点的精确距离.所以我们要表示 ...