工作流引擎下基于表达式Tree复杂验证的实现
工作流引擎下基于表达式Tree复杂验证的实现
在工作流开发中,用户根据Form模板引擎自定义Form表单时,需要动态定义对Form表单中的值进行验证来控制来验证用户的输入。
Form表单字段验证无法就是长度,是否为空,格式是否正确等等,这些我们可以通过在创建Form表单时,根据控件(自定义各种Form控件)的类型来验证。不过这只能对Form表单进行控制,如果要动态控制Form表单所关联的业务逻辑呢?这时,我们就应该分析业务逻辑。在一般的业务逻辑进行验证时,一般就涉及比较运算符(<、>、>=、<=、=)和算法运算符(+、-、*、/)的验证。这就需要基于表达式的验证方式。
场景:假如用户在定义工资表单(Wage Form)时,有一个工资的表单框txtWage,如果要实现员工工资(类别:初级软件工程师)满足两种条件:
(1)最低工资不能低于1500
(2)基本工资*工资系数1.1(不固定)+项目奖金500(不固定)不能超过4000。
仔细分析,就可以看出:
上面的两个条件可以等价位 : Wage>=1500&&Wage*1.1+500<=4000
那么如果直观的实现上面的表达式,而又方便修改、删除等操作。根据我们学的数据结构就会联想到二叉树,也就是基于树的实现。问题有来了,如何实现树的结构呢,而又能够方便修改,添加等操作。首先想到了TreeView控件,但发现TreeView控件不能编辑和自定义添加和修改标签。这是只能考虑自己构建树的结构。下面来看一下,我在工作流引擎中实现的一个Demo。
上面图片实现了树的收缩、展开、表达式的自定义,支持嵌套定义表达式,通过弹出创建窗口(或者DIV层)来实现,弹出窗口包括数据类型,source定义(表达式左边),target定义(表达式右边)。每一个FieldValidation可以包括多个Expression,这多个Expression之间可以是&&或者 || 等操作,最顶层的Expression是比较运算符操作,子Expression是算术运算符操作。
那么上面数据结构式如果实现的呢?仔细发现其实就是一个标准二叉树结构。相当于有三种节点类型:Expression、Source、Target,我们可以用一个字段来标识这三种类型。这种方式最容易实现。但在我们的项目功能设计时,数据结构已经设计好了,是通过对象List集合嵌套来定义。Validation 类,Expression类、Leaf类。Validation有一个Expression的List集合, Expression类中有一个Leaf类型的Source属性和一个Leaf型的Targe属性,同时有一个Parent字段来标识他的父节点,而Leaf类有一个Expression类型的属性Expression,用来关联子Expression,同时也有一个Parent字段来标识他的父节点Expression。
接下来要实现树的遍历就涉及到树的遍历:递归查找父节点、子节点、递归获取某一个Expression的层次(限制Expression深度为5)。而在递归上面,我纠结了很几天,总是出现各种错误,最终还是通过理解递归原理来实现的。要递归实现:首先要找到出口:也就是当程序满足某种条件时就跳出函数,返回其查找的值。在递归时,函数中局部变量是通过栈的方式保存起来。
先来看一个获得某一个Expression的深度的方法。
假如上面的图片中树中第一顶层Expression的Target有一个子Expression(节点4),如果要获得这个Expression的Level,栈中的结构是怎么样的呢? 首先从节点一开始,发现exp1的RefNo!=RefNo,此时temp=1,继续递归下去,直到节点3,,还没有找到,然后就开始回溯。此时栈中结构。
栈底
|
栈底
|
当节点3RefNo!=RefNo不成立时,Exp3,Temp3出栈;就回溯到节点2,发现节点2 中targe也不满足要求,Exp2, Temp2出栈;回溯到节点1,然后执行节点1的target节点,发现targe.Expression.RefNo==RefNo 如是就记录Expression_Levl=temp+1=2,然后跳出循环。Expression_Levl就是要获取的值。
在软件开发中,树、表达式、递归是经常会用到的,层级菜单的创建,权限的设计等等,都会设计树、递归,其实只要掌握了递归的原理,一切都会迎刃而解的。
下一篇:基于微软 ResourceProvider机制多语言的实现。