`
ahuaxuan
  • 浏览: 639509 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

土制状态机在工作流引擎中的应用

阅读更多
/**
  * @author : ahuaxuan
  * @date 2009-10-27
  */

很早之前(应该是一年以前),ahuaxuan在用dfa实现文字过滤一文中使用确定有限自动机实现了词典的高速查询。其实在当时那段时间里,由于对状态机有了一定的研究,ahuaxuan也触类旁通的理解了工作流引擎的核心体制。于是当时就用python写了一个小巧的工作流引擎的示例,在这之前ahuaxuan没有看过任何工作流引擎的实现,该实现纯属思维的自我延伸。

现在我来说说我的实现。
状态机的本质是状态的迁移,即从A状态+某个动作===》B状态。到这里我们还要来看看这张图。

从这张图中我们可以看到,状态(大写字母)+动作(小写字母)可以到达新的状态。那么对于程序员来说,我们要做的就是将这种机制用程序表达出来。比如说我们最常想到的是什么?矩阵!

这很好理解,但是对于工作流引擎来说,由于状态的迁移涉及到:当前状态+动作+条件===》新状态。
所以用二维的矩阵无法表示出这种逻辑。那么我们可以将矩阵中的元素替换为条件数组。
这样,我们可以通过当前状态+动作得到一个条件数组,然后再遍历这个条件数组,条件数组中的元素即是条件和满足条件的下一个状态(虽然本质上是一个三维数组,但是在这里还是看成矩阵+数组元素比较符合逻辑)。



这里需要画一个图,一个矩阵,矩阵中的元素是一个条件数组

没错,这是一种方案,但是这里有一个问题,那就是每次做状态迁移的时候,我们必须知道某个状态在矩阵一维上的index,已经某个动作在矩阵二维上的index.有了这两个index我们才能得到条件数组。所以这里还有一个繁琐的转换操作操作。来看一段伪代码:
conditions = matrix[getStatusIndex[‘A’], getActionIndex[‘a’]]
For condiction in conditions:
       If condition match input:
              Return Condition.nextStatus


核心流程大概就是这样,当

那么除了矩阵这种数据结构,我们还有其他的数据结构可以用来表示: “当前状态+动作+条件===》新状态”吗。

当然有,那就是使用树结构。在了解了三维数组的实现之后,再来看树实现,应该和容易了,那就直接上图, 将上图的矩阵转换成树结构之后,我们可以得到如下的树结构。



那现在我们来审视一下现在的问题,打个比方,我们现在手里有两张牌,一张是状态A,一张是动作a,我们如何通过这两种牌来得到条件集合呢,最简单的方法是首先遍历第二层节点,找到A,然后再遍历A的子节点,找到a。通过两次for循环找到了条件集合,而条件集合中包含着下一个状态。

那么有没有更简单更快速的方式可以直接找到条件集合,而直接跳过两次遍历呢。有,ahuaxuan的想法是tree+hash.也就是通过A的hash值,我们可以直接找到tree上的A节点。然后再通过a的hash值,我们可以直接找到A的子中的a节点。得到a节点之后我们就可以条件集合。那么我们可以用什么样的数据结构来实现一个这样的模型呢。这里面有hash运算,那么我们首先选择HashMap来创建这么一颗树。

我们来看一下这个定义:
Map<String, Map<String, Map<String, List<Transition>>>> dfa。


那么我们如何根据A,和a来得到一个条件集合呢?
Dfa.get[“processName”].get[“A”].get[“a”]
通过这样的方式,我们就可以根据当前状态和当前的动作得到一个条件集合。然后遍历这个条件集合就是找到满足“输入“的条件,该条件会指向下一个状态。

我们来看一下代码实现:
首先我们来构造这么一个状态机:
private void constructDfa(List<WfProcess> processList) {
		for (WfProcess pro : processList) {
			Map<String, Map<String, List<Transition>>> pmap = new HashMap<String, Map<String,List<Transition>>>();
			dfa.put(pro.getName(), pmap);
			
			for (State sta : pro.getStates()) {
				Map<String, List<Transition>> smap = new HashMap<String, List<Transition>>();
				pmap.put(String.valueOf(sta.getName()), smap);
				
				for (Action action : sta.getActions()) {
					List<Transition> transitions = new ArrayList<Transition>();
					for (String transName : action.getTransNames()) {
						transitions.add(pro.getTransitions().get(transName));
					}
					
					smap.put(String.valueOf(action.getName()), transitions);
				}
				
			}
		}
	}


接着我们来看看如何根据这个状态机来做状态迁移:

public String getNextState(String processName, String stateId, String actionId, Map<String, String> conditions) {
		
		List<Transition> transitions = dfa.get(processName).get(String.valueOf(stateId)).get(String.valueOf(actionId));
		
		for (Transition trans : transitions) {
			if (match(trans.getConditions(), conditions)) {
				return trans.getToState();
			}
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append("There is no state for process : ").append(processName);
		sb.append(", stateId : ").append(stateId);
		sb.append(", actionId : ").append(actionId);
		sb.append(", conditions : ").append(conditions);
		
		throw new WorkFlowStateException(sb.toString());
	}


通过这种tree + hash的方式,我们可以很容易的进行状态的迁移,不需要那么多for循环。但是for循环确实有这样的实现。

今天早上下载了osworkflow的代码,稍微看了一下AbstractWorkflow的doAction方法。
发现osworkflow就是通过循环来实现状态的迁移的,比如说上图中树结构的状态可以用以下伪代码:
For state in states:
	If state.name == inputStateName:
		For action in state.actions:
			If action.name == inputActionName:
					for transition in action.transitions:
	…………………………………………………………………



通过这种方式,用户传入inputStateName和inputActionName, osworkflow得到了一组transition,并根据条件选择某个transition, 这样也实现了状态的转移。

从这里面可以看出,osworkflow是利用广度优先的原则,先找到符合条件的state,然后再找到符合条件的action,以此类推。

说到这里,通过这种状态机实现工作流引擎的方式基本的完全的,较为清晰的呈现在我们眼前了。

未完待续

  • 大小: 16.2 KB
  • 大小: 21.8 KB
4
0
分享到:
评论
2 楼 ahuaxuan 2009-11-02  
把动作和条件合并在一起的做法不太流行


动作是一个操作,条件是对业务数据的一个抽象。
比如动作是一个类的方法,而条件是方法的参数决定的,这样更合理一些
1 楼 melin 2009-11-02  
打算写一个小的工作流引擎:
定义的流程定义为:
<processdefine name="TestFlow" version="1.1.1" description="">
	<Activitys>
		<Activity id="start" type="start" name="开始" description="">
			<SplitMode>XOR</SplitMode>
		</Activity>
		<Activity id="A01" type="manual" name="申请" description="">
			<SplitMode>XOR</SplitMode>
			<JoinMode>XOR</JoinMode>
			<participantType>organization</participantType>
			<participantList>
				<participant id="910150" name="俞文琦" type="person"/>
				<participant id="910115" name="李强" type="person"/>	
			</participantList>
		</Activity>
		<Activity id="A02" type="manual" name="审核" description="">
			<SplitMode>XOR</SplitMode>
			<JoinMode>XOR</JoinMode>
			<participantType>process-starter</participantType>
		</Activity>
		<Activity id="end" type="finish" name="结束" description="">
			<JoinMode>XOR</JoinMode>
		</Activity>
	</Activitys>
	
	<Transitions>
		<Transition id="" from="start" to="A01" name="">
			<IsDefault>true</IsDefault>
		</Transition>
		<Transition id="" from="A01" to="A02" name="">
			<IsDefault>true</IsDefault>	
		</Transition>
		<Transition id="" from="A02" to="end" name="">
			<IsDefault>false</IsDefault>
			<Expression>optId==1</Expression>
		</Transition>
	</Transitions>
</processdefine>


查找当前环节的后续环节为:
public Transition[] getNextTransitions(String aid) {
		Transition[] arr = null;
		List<Transition> list = new ArrayList<Transition>();
		Iterator<Transition> it = this.transitions.iterator();
		while (it.hasNext()) {
			Transition trans = it.next();
			if (trans.getFrom().equals(aid))
				list.add(trans);
		}
		arr = new Transition[list.size()];
		for (int i = 0; i < arr.length; ++i) {
			arr[i] = ((Transition) list.get(i));
		}
		return arr;
	}


查找满足条件的transition
public static List<String> getPossilbeListNormal(WFProcessInstance pi,
			Activity actDef, Transition[] transarr) {
		List<String> possibleList = new ArrayList<String>(4);

		if (actDef.getSplitMode().equals("AND")) {
			for (int i = 0; i < transarr.length; ++i) {
				possibleList.add(transarr[i].getTo());
			}
		} else {
			Transition[] oktrans;
			int i;
			if (actDef.getSplitMode().equals("XOR")) {
				oktrans = getXORModeNextTrans(transarr, pi.getRelDataMap());

				Transition tran = findMostOKByPrority(oktrans);

				possibleList.add(tran.getTo());
			} else if (actDef.getSplitMode().equals(DefineConst.SPLIT_MODE_OR)) {
				oktrans = getORModeNextTrans(transarr, pi.getRelDataMap());

				if (oktrans.length == 0) {
					return possibleList;
				}

				for (i = 0; i < oktrans.length; ++i) {
					possibleList.add(oktrans[i].getTo());
				}

			}

		}

		return possibleList;
	}

相关推荐

    求生之路插件开局给幸存者药丸土制等随机物品

    求生之路插件开局给幸存者药丸土制等随机物品

    kno3是什么物质的化学名称.pdf

    总结起来,硝酸钾,这个看似简单的化学物质,却蕴含了丰富的化学知识,并在实际应用中展现出其重要价值。无论是科学研究还是农业生产,硝酸钾都是我们无法忽视的一种物质。理解其性质、制备方法以及应用范围,有助于...

    企业安全生产基础知识.doc

    【企业安全生产基础知识】是企业在日常运营中必须重视的重要领域,涉及到劳动者的安全和健康保障。以下将详述其中的关键知识点: 1. **法律法规遵循**:企业需依据《安全生产法》等法律法规,制定相应的规章制度,...

    特种设备述职报告.docx

    5. **行风建设与业务结合**:将行风建设融入到特种设备监察工作中,通过移址办公,提高了行政许可项目的办理效率,体现了政风行风建设的成果,提升了服务企业的质量和水平。 总的来说,这份述职报告展示了如何在...

    中职烹饪教师资格证、教师招聘面试试讲教案-调料.pdf

    * 甜味调料:甜味调料在调味中的作用仅次于咸味调料,在调味中可以单独成味,同时也可调制许多复合味型,还起到增强鲜味、抑制辣味、苦味和涩味的作用 其中以果糖甜味最佳。食糖按制造方法不同可分为机制和土制糖,...

    小学生森林防火教育PPT课件.pptx

    这在小学的防火教育中尤为重要,因为教育孩子们了解森林火灾的危害和预防措施,能从小培养他们的环保意识和安全意识。 森林火灾的危害多方面体现: 1. 烧毁林木:森林火灾首先会摧毁大片森林,导致树木死亡,破坏...

    烟花爆竹打非工作汇报 (2) .docx

    通过以上分析可以看出,**县**镇在烟花爆竹“打非”工作中采取了全方位、多层次的措施,取得了显著成效。然而,要彻底根除这一现象,还需不断加强宣传教育、完善监控体系、加大执法力度,并结合改善当地经济发展水平...

    论文研究 - 生土的NaOH活化:NaOH含量对干燥动力学的影响及其建模

    这项工作旨在确定氢氧化钠浓度对用生黏土制得的砖的干燥动力学的影响,并对该动力学进行建模。 结果表明,由于缺乏游离水,干燥动力学受水的扩散控制。 干燥时间随NaOH含量的增加而线性增加,而体积收缩率则下降,...

    洞石系列.pdf

    总之,洞石系列作为技术标签中的一个重要元素,不仅体现了天然石材的美感和质感,还展示了其在建筑和装饰领域的广泛应用和创新。通过选择不同系列、颜色和规格的洞石,设计师可以根据项目的特性和客户的需求,打造出...

    铁路工程特殊岩土勘察规程

    - **极限胀缩性:** 原状土试样或扰动土制样在特定状态下的最大膨胀率和收缩率。 - **前期固结压力:** 土体历史上承受过的最大垂直有效压力。 - **压缩层的计算深度:** 地基土在荷载的竖向附加应力作用下产生固...

    初中物理趣味竞赛题.doc

    16. 布朗运动:悬浮在液体中的微粒的无规则运动,证明了分子运动论的正确性。 17. 光年定义:光在一年内传播的距离,是衡量天文距离的单位。 18. 光的漫反射:电影银幕的材质使光线向各个方向反射,使得观众从不同...

    山西省大同市浑源县第七中学2020_2021学年高一历史下学期第一次月考试题2021052102133

    3. 西欧封土制:封君封臣制度是西欧中世纪的一种政治制度,体现了封建社会的特点和权力结构。 4. 古代文明的扩张:文明的扩张不仅连接了不同的文明区,也加速了社会的变迁,例如封建帝国的解体,但和平交流并不是...

    吨级生活垃圾资源化处理工程.docx

    废塑料可制成颗粒或板材,营养土制成肥料,废钢铁回炉,建筑垃圾则制成混凝土砌块,实现了垃圾的资源化利用。 此规划考虑了现实需求和长远发展,设计规模与当前垃圾产出量接近,留有余地以应对未来增长。通过最大化...

    200x年特种设备安全监察工作计划.docx

    ### 200x年特种设备安全监察工作计划解析 #### 一、加大《特种设备安全监察条例》宣传力度 1. **目标**: 提高全社会对特种设备安全的认知度,增强公众的安全意识和自我保护能力。 2. **手段**: - 利用多种媒介...

    500吨级生活垃圾资源化处理工程.docx

    该设备在2000年获得国家专利,并经过数年的研发,技术已成熟,被认为达到国际领先水平。 项目规划以日处理500吨生活垃圾为目标,适应当前城市人口规模。处理流程包括先发酵后分拣,利用太阳能进行好氧和厌氧发酵,...

    高一地理 第三单元 3.7陆地环境的组成 土壤非选择题练习试题 人教版

    3. 生物作用:在自然环境中,生物是连接有机界和无机界的中心环节,通过生物循环,将有机物质转化为无机养分,供给植物生长。 4. 土壤的本质特征:土壤的肥力是其最本质的特性,因为它能够支持植物生长。具有肥力的...

    常见易读错的汉字.doc

    这篇文档主要列举了一些常见但容易读错的汉字,并按照它们在日常生活用品和祭祀礼仪中的用途进行分类。这些汉字不仅展示了中国古代的文化和工艺,同时也反映了古人生活的方方面面。 在日常生活用品方面,文档提到了...

    2丁丁冬冬学识字——学生学习课件

    - **时间**:通常在傣历六、七月份,公历四月中旬。 - **庆祝方式**:人们穿着节日盛装,相互泼水,预祝新的一年吉祥、幸福和健康。 - **文体活动**:泼水节期间还会举办各种文体活动,如放高升(土制火箭),这是一...

Global site tag (gtag.js) - Google Analytics