我们使用过工作流设计器的朋友一定都有这样的经验,流程设计器设计出来的流程图如果用XML来存储,是一个类似层次结构的数据模型,直接对这种数据模型进行处理并不是很方便(但是我也见到很多直接在XML结构上面进行后台引擎处理的工作流系统,所以我这里说描述的方法不一定是最好的),所以我没有直接在流程图XML上面进行数据操作和处理,而是把用XML存储的流程图数据转换成一种简单的链表结构并存储在数据库中,具体的方法请参考JWFD的相关设计说明文档,那么我要说的关键的问题是我们在开发流程设计器的时候,对于设计器存储流程图用的XML的数据模型最好简单一些,只在流程图的XML文件中存储流程图的拓扑结构数据和必要的嵌入数据,其他的控制数据可以保持在后台的数据库中,这样做的好处是很明显的,因为我们在对XML文件进行转换和提取的时候,简单的XML文件结构对于编程进行SAX和DOM解析有很大的好处,另外如果XML文件的数据比较少,我们在流程设计器中需要开发的模块和代码也就相应的少一些,这会大大降低我们开发工作流设计器的难度。。。。比如说用FLEX和JS的流程设计器
(我的CSDN博客资源下载里面有几种这些的带有源代码的设计器例子,大家可以下载并自行修改),由于FLASH和JS本身的功能限制,我们做出了的设计器不可能太复杂,而且如果太复杂,在浏览器里面运行这些设计器速度就会变得很慢,效率也很低
http://download.csdn.net/detail/comsci/5232835 流程设计器下载
如果我们在数据库中存储了流程图的拓扑结构,那么下一步应该怎么办呢? 大家都有过封装代码变成函数模块的经验,那么我们在这里开始利用数据库系统本身的SQL语句对这些存储在数据库中的流程图数据进行分析和操作,比如说,我们要查询流程图中某个节点的前驱节点和后驱节点(前一个节点,后面一个节点),那么我们就可以利用SQL语句里面的select语句来编写这些功能SQL操作代码,例如在JWFDv0.96.3版本中:我们封装一个函数return_nextStep(),这个函数的功能就是返回输入节点的下一个节点,输入参数是当前节点ID和当前流程图ID,那么输出结果就是当前流程图中对应当前节点的下一个节点ID,类型是string
public String return_nextStep(String step_id, String graph_id) {
String Step_id_next = "";
try {
db_conn.rs = db_conn.stmt.executeQuery
("select to_step from edge_control where from_step = '" + step_id +
"' and graph_id = '" + graph_id + "' ");
if (db_conn.rs.next()) {
Step_id_next = db_conn.rs.getString("to_step");
}
}
catch (Exception e) {
System.out.println(e+"返回下一个节点异常");
}
return Step_id_next;
}
类似的,我们在JWFD工作流系统中实现并封装了大概有50个以上的SQL操作函数,包括查询流程图各种类型节点的操作,设置流程节点状态和查询流程节点状态等相关操作函数,用于构造工作流引擎模块的基础流程图处理方法,同时,这些SQL-API也是流程引擎自动运行控制的基础API,当然,我前面说的如果用户直接用XML层次数据模型进行流程图的操作和处理,这个类似的封装流程图操作函数的过程也是需要的。。。。
JWFD开源工作流的API和二次开发说明文档 下载
现在我们越来越接近实现工作流引擎的核心部分-ARC(自动运行控制器)了,经过前面的工作,在我们的系统平台中已经有了一套可以对工作流的拓扑结构进行操作的函数(方法)群,那么现在我们需要设计一个算法来调用这些函数,以实现对流程的自动运行控制(自动运行控制这个词汇是来源于自动化领域的,我在这里借用一下,不一定合适,另外,ARC或许也可以被称为虚拟演进,我在一些专业论文里面经常看见工作流虚拟演进这个词汇,可能我估计这个虚拟演进就是自动运行控制。。。也许吧。。我猜的)
在设计这个算法之前,让我们在脑海中对流程图建立一个模型,工作流就是一副图,一副由很多个点连接在一起形成的网络图,从本质上来讲,流程图和其它系统的拓扑结构图是相似的,比如说我们互联网的拓扑结构,我们的神经网络结构图,那么怎么对这样一种图形结构的数据进行自动处理呢? 借用其它领域的一个典型方法来描述这个概念
假如 我们需要坐一部汽车去郊游野外(流程图),我们需要走很多景点(节点),从哪里走呢? 国道或者高速路(节点间连接线),那么这部汽车和驾驶者是分别表示什么呢? 汽车的核心是发动机,发动机给我们这次旅行提供动力,而驾驶员是指明方向的。。。而每个景点的门票就是我们的通行证。。。总结一下,要完成这次旅行我们就需要-汽车(发动机)-导航员(驾驶员)-门票(路费)-景点(道路),当然还有我们这些乘客(用户)
那么,我们的这个算法里面同样也需要这几种东西 -- 发动机-导航者-门票 推导一下 就是 动力源-导航控制-关卡 用算法的语言来对照一下,什么语句可以扮演这三种角色呢?
----------------------------------------------------------------------------------------------------------------------------
动力源:当我们的一系列语句要按照某种次序不停的运行,用什么语句呢? 循环。。嗯,循环语句 满足某种条件而在循环体内部的语句可以不停的运动
导航控制: 当我们的语句需要改变运行方向的时候,什么语句可以发挥作用呢? 判断,对吧,判断语句,通过判断某种条件是否成立,而改变语句的运行方向
关卡:当我们的语句运行完一遍的时候,需要通过某种方法运行到同一个结构的另外一个层次继续运行,用什么方法呢? 递归方法。。。特别是对于拓扑结构这种比较复杂的数据结构,用递归方法来通过不同的关卡,多次运行,直到走完最后一个景点。。。
----------------------------------------------------------------------------------------------------------------------------
那么,循环-判断-递归这种算法结构就出现了,事实上,这种结构并不是我一开始就很了解的,而是已经在把JWFD的自动运行控制器都修改了若干遍之后,后来才慢慢领悟到的,这种算法的最初模型却是来自于图论的广度优先遍历算法,但是后来慢慢的经过不断的修改,已经不像是一个遍历算法了。。。。
再简单一点的描述,ARC就是图论的遍历算法+节点状态值控制的一个综合体,精简下来,只有三个地方是最核心的 for-if-recursion,之所以要把这个算法精简下来,是因为我们还想对ARC算法进行更深一步的研究,以便实现流程的自适应自动运行控制,而不仅仅是自动运行控制,在精简ARC算法得出循环-判断-递归结构之后,我们的思路如果扩展一下,就可以发现-在循环-判断-递归结构中还可以增加一项比较独特的语句-跳转-JUMP,Jumper这部电影,不知道大家看过没有,星球大战前传的男主角演的,帅哥在这部戏里面有种特异功能-会空间跳跃,一瞬间就跳到相隔很远的另外一个地方,比如说从自己的屋里跳到银行的地下金库里面,然后带回大把钞票,哈哈,多爽啊。。。其实这个跳跃的功能是我们很多人梦寐以求的,虽然我们在现实里面无法实现这种能力,但是在计算机里面,特别是在流程系统里面,我们可以应用这些跳跃的能力,语句是这样的 IF(引擎感觉不爽 = TURE) { JumpTo 另外一个节点},虽然美国人在设计出带有GOTO语句的BASIC语言之后,发现GOTO语句会引起程序结构的混乱而终止在计算机语言中使用类似GOTO语句的跳转语句,但是这并不妨碍我们在工作流系统中应用有条件的跳转语句来增强我们的ARC结构模型,其实我想说的一句话是: 适当的使用跳转语句,可以开发出某些非常强大的程序来。。。
当循环-判断-递归结构加上有条件跳转语句之后,这个算法结构就变成一个具有反馈能力的自动运行控制结构,这个结构有什么用处呢? 还是以前面的例子来说明,假如说我们出去旅游的时候,走到一个景点的时候,发现这个景点没有开放,而连接这个景点的前方道路又没有修好,那么怎么办? 我们就可以告诉驾驶员,沿路返回,从我们出发的地方重新走另外一条道路,去往另外一个景点继续我们的行程。。。。。很好理解吧。这就是带有反馈功能的自动运行控制器的设计思路。再简单不过了,国内的开源工作流Fireflow的引擎里面就带有JUMPTO这个功能,请有兴趣的朋友去咨询一下fireflow的作者非也同学。。
其实做工作流系统挺有趣的,我们面对一个复杂的,不可预测的流程图的时候,就需要更为强大的算法和程序才能够处理这些数据结构,那么为了开发出这些更为强大的程序和算法来,我们就必须思考,不拘一格的思考。。。。。。这也许有助于我们实现人工智能系统。。。。但愿这条路能够走通
这篇博客就暂时写到这里,思考还将继续。。。。。。。。大家一起努力吧。