从工作流状态机实践中总结状态模式使用心得

 作者:banq 发表时间:2003年12月07日 00:10 回复
原贴网址:http://www.jdon.com/jivejdon/thread/10981.html

 

状态模式好像是很简单的模式,正因为状态好像是个简单的对象,想复杂化实现设计模式就不是容易,误用情况很多。

我个人曾经设计过一个大型游戏系统的游戏状态机,游戏状态可以说是游戏设计的主要架构,但是由于系统过分复杂
和时间仓促,并没有真正实现状态模式。

目前在实现一个电子政务项目中,需要进行流程状态变化,在电子政务设计中,我发现,如果一开始完全按照工作流
规范开发,难度很大,它和具体项目实践结合无法把握,而且工作流规范现在有wfmc,还有bpml,选择也比较难。因
此,我决定走自创的中间道路。

因为,我需要做一个状态机API,或者说状态机框架,供具体系统调用:类如公文流转应用或信息发报送应用等。

好的状态模式必须做到两点:
1. 状态变化必须从外界其它逻辑划分出来。
2. 状态必须可方便拓展,对其它代码影响非常小。

要做到这两点,必须先明确状态变化机制,状态变化实际是由Event事件驱动的,可以认为是Event-condition-State,
在MVC模式一般是Event-condition-Action实现。状态模式需要封装的是Event-condition-State中的condition-State
部分。

清晰理解状态和流程的关系也非常重要,因为状态不是孤立的,可以说和流程是点和线的关系,状态从一个方面说明
了流程,流程是随时间而改变,状态是截取流程某个时间片。因此,必须明白使用状态模式实现状态机实际是为了更
好地表达和说明流程。

状态和流程以及事件的关系如下:

                 |Event___currentState__|______newState___

图中表示了是事件改变了流程的状态,在业务逻辑中,经常发生的是事件,如果不使用状态模式,需要在很多业务逻
辑处实现事件到状态判定和转换,这有很多危险性。

最大的危险是系统没有一个一抓就灵的主体结构,以那个游戏系统为例,在没有状态模式对状态提炼的情况下,状态
改变由每个程序员想当然实现,导致每个程序员开发的功能在整合时就无法调试,因为这个程序员可能不知道那个程
序员的代码在什么运行条件下改变了游戏状态,结果导致自己的代码无法运行。

这种现象实际上拒绝了项目管理的协作性,大大地拖延项目进度(程序员之间要反复商量讨论对方代码设计)。从这
一点也说明,一个好的架构设计是一个项目快速成功完成的基础技术保证,没有这个技术基础,再先进的项目管理手
段也是没有效率的,或者是笨拙的。

状态模式对于很多系统来说,确实是架构组成一个重要部分。

下面继续讨论如何实现一个好的状态模式,为了实现好的状态模式,必须在状态模式中封装下面两个部分:
1. 状态转换规则(行为)
2. 状态属性(属性)

状态转换行为有两种划分标准:
1. run和next两个行为,run是当前状态运行行为,next是指在Event参与下,几种可能转向的下一个状态。
2. stateEnter和stateExit, 状态进入和状态退出。

如果用进入一个个房间来表示状态流程的话, 第一种分析是只重视着“在房间里”和“如何转入下一个房间”,这两种行
为一旦确定,可以被反复使用,进而一个流程的状态切换可以全部表达出来。

第二中分析方法有所区别,只重视进入房间和离开房间这个两个行为,同样,这种模型也可以被反复利用在其它房间,
一个流程的状态切换也可以全部表达出来。

具体选择取决于你的需求,比如,如果你在进入一个状态开始,要做很多初始化工作,那么第二种模型就很适合。

状态变化都离不开一个主体对象,主体对象可以说包含状态变化(行为)和状态属性(属性),假设为StateOwner,
StateOwner有两个部分组成:Task/事情和状态。任何一个Task/事情都会对应一个状态。

这样,我们已经抽象出两个主要对象:状态State和StateOwner。

为了封装状态变化细节,我们可以抽象出一个状态机StateMachine来专门实现状态根据事情实现转换。

这样,客户端外界通过状态机可访问状态模式这个匣子。在实践中,外界客户端需要和状态机实现数据交换,我们把
它也分为两种:属性和行为。

其中属性可能需要外界告诉状态状态变化的主体对象Task,解决状态的主人问题,是谁的问题;行为可能是需要持久
化当前这个主体对象的状态到数据库。

这两种数据交换可以分别通过StateOwner和StateMachine与整个状态机实现数据交换,这样,具体状态和状态切换也
和外界实现了解耦隔离。

因此好的状态模式实现必须有下列步骤:
(1)将每个状态变成State的子类,实现每个状态对象化。
(2)在每个状态中,封装着进入下一个状态可能规则,这些规则是状态变化的核心,换句话说,统一了状态转换的规则。
具体可采取run和next这样的转换行为。

下面是一个子状态代码:


public class Running extends StateT{
Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月08日 11:16 回复
iceant 发表文章: 462/ 注册时间: 2002年10月13日 22:32
我原来的想法是使用 Event 为中心的处理机制: Event + Listener

拿你说的从一个房间进入另一个房间来说:
临界事件,就是跨过门。所以,在系统中,一般会有三个处理方法:

prePassThroughDoor();
doPassThroughDoor();
postPassThroughDoor();

在每个事件,都可以加入相应的 Listener. 这样可以做到系统的可扩展性。

但是这有个问题,就是所有的事件都必须去手写实现,这会很麻烦。
现在有 AspectJ, 如果我对 AspectJ 的理解没错的话,它很容易就可以实现这一点。你可以在任一方法调用之前,或之后,加入相应的事件点,在事件点上加入 Listener 机制。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月08日 11:34 回复
chenye99 发表文章: 15/ 注册时间: 2003年06月23日 10:20
> public class Running extends StateT{
>
> //
> public void run(StateOwner stateOwner){
> stateOwner.setCurrentState(this);
> }
>
> //转换到下一个状态的规则
>
> //当前是Running状态,下一个状态可能是暂停、结束或者强
> 仆顺龅?> //状态,但是绝对不会是Not_Started这样的状态
> //转换规则在这里得到了体现。
> public State next(Event e) {
> if(transitions == null){
> addEventState(new EventImp("PAUSE"), new
> ), new Suspended());
> addEventState(new EventImp("END"), new
> ), new Completed());
> addEventState(new EventImp("STOP"), new
> ), new Aborted());
> }
> return super.next(e);
> }
>
> }
>
>
>
> 外界直接调用
> StateMachine的关键方法transition;实行状态的自动转变。
对于return super.next(e)的处理方式不是很清楚,可否给出StateT的next方法的代码?
 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月08日 18:00 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
StateT的next方法如下:

public State next(Event e) throws InvalidStateException{    logger.debug(

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月09日 11:45 回复
chenye99 发表文章: 15/ 注册时间: 2003年06月23日 10:20
我理解Suspended、Completed、Aborted类也都是StateT类的子类,不知是不是这样?
在StateT类的next方法中,是根据事件来进行跳转,假设可能又两个状态都存在PAUSE事件,又怎么区分呢?
在刚看到方案时我的感觉是StateT子类中next方法应为return super.next(this,e),不知是不是我的理解有问题?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月09日 12:19 回复
wild fox 发表文章: 55/ 注册时间: 2003年03月19日 17:34
如果你的状态State非常多,而且Event也非常多,并且每一个Event都可能在很多个不同的State中起作用并使系统进入到不同的State,那么,是不是需要在每一个State的next()方法中都要用代码写出来呢?

另外假如对于
>(StateA)---[Event1]--->(StateB)
过了一段时间需要>(StateA)---[Event1]--->(StateC)那么又要到代码里面去修改了?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月09日 14:09 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
to chenye99
你的理解基本没错,从实际角度考虑,没有两个状态存在同样事件的,因为事件相当于外界给予能量,主体有了新能量,本身能量会发生变化,这类似能量守恒定律。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月09日 14:27 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
to wild fox
状态模式只关注某个状态,以及这个状态可能转入的下几个状态。

我们不必在乎一个流程有很多状态,一个个分开对付就可以了。

对于
>(StateA)---[Event1]--->(StateB)
改变到
>(StateA)---[Event1]--->(StateC)

我们所要改变的只是StateA这个类的next这个方法代码。当然,也可以将next方法中可能对应的下一个状态配置在配置文件中,这样就不需要改代码了。

重要的是,我们使用状态模式封装了状态和状态转换规则,使他们没有互相混乱地纠缠在一起。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月09日 18:00 回复
wild fox 发表文章: 55/ 注册时间: 2003年03月19日 17:34
我现在在做的项目就是采用的State + StateMachine模式的,不过我们采用了配置文件来设置源State和目标State之间的切换,以次来增加系统的灵活性,虽然有一定的效果,可是,我们不但需要关心当前State,驱动事件,还需要考虑源State,以及当前State的一些条件来决定目标State,可是各个State中还是有很多if...else if...else... ,而且,当源State 和目标 State之间的切换规则发生了变化时,修改了配置文件以后,往往还是需要对相应的代码进行修改,怎样做才能在State间的切换规则发生改变以后尽可能地少对代码进行修改呢?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月10日 08:44 回复
chenye99 发表文章: 15/ 注册时间: 2003年06月23日 10:20
“以及当前State的一些条件来决定目标State”可不可以理解为当前state也存在不同的状态?如果是这样,是否有将状态进一步细化的可能?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月10日 09:18 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
to wild fox
你们存在的这个情况,说明没有真正用好状态模式,这再一次说明,模式用好真的不容易。

用好状态模式的前提必须有对系统状态和事件最本质的认识和抽象。

但就我举的这个实例,next方法中实际是给一个Hashmap赋值,所以,这个过程完成可以使用XML配置。

要做到方便的XML配置,前提还是必须将状态和状态转换规则独立分离开,否则XML配置将只是一种形式。

XMl配置是解耦后跟进一步的表现,如果你的逻辑能够做到XML配置即可运行,那么说明你之前的解耦设计已经非常成功。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月10日 09:19 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
to chenye99
状态有子状态,类似树形结构。使用状态模式处理子状态会更复杂。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2003年12月26日 17:40 回复
天下为公 发表文章: 31/ 注册时间: 2003年07月15日 17:09
EventImp与
Event的关系,请说一下,我不是太明白?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2004年09月24日 10:03 回复
wm_creat 发表文章: 10/ 注册时间: 2003年09月13日 16:20
那位老兄说得很对啊,你也知道1个事件是由一个事物驱动,一个事物同一时间只有一种状态,一个状态却可能对应0到n个事物,如果你存储状态改变的数据结构没有相关事物信息,将无法区分这个状态是A事物驱动的还是B事物驱动的,换句话说,这个数据结构的主键是:事物id,状态id,而不是一个状态id就可以的,不然必定违反数据库原理!

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月26日 09:47 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
您好,请问banq,StateOwner类的源代码是什么?对StateOwner的作用不甚理解.谢谢

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月26日 09:54 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
to banq,banq老师,能否将您上次讲的公文流转的例子讲的再详细些,目前正在做些这方面的尝试,很多地方理解的不太好.特别是StateOwner这个对象的作用不是太理解,能否给出代码并解答,实在是不胜感激啊!

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月26日 10:06 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
具体研究可看看工作流方面的东西,基于状态模式,但是更大,有个开源osworkflow还是不错。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月26日 10:41 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
to banq,
谢谢banq老师的这么迅速的回复。有个疑问还想请教您。我觉得各个状态转换应该有用户的参与,在上面的讨论中,用户好象没有提及(还是被Event代替?即使是被event代替的话,event中也应该要包含用户信息啊,不知这样的理解对不对?)。如果用户信息要被加入,是放在State的各个子类中还是放在Event里?是设置为一个actor还是一个role?谢谢!

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月27日 10:25 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
>状态态转换应该有用户的参与
注意耦合,状态模式的特点就是用事件作为输入信号,状态作为输出信号,而事件的发生则是由用户触发的:

用户---->事件--->状态。

所以用户离我们讨论的状态模式很远,不用拉进来。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月28日 09:26 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
to banq,
谢谢banq老师。banq老师在前面谈到怎样将状态机与应用逻辑解耦。但在实现时,状态机又必须跟应用逻辑结合,特别是状态机进行状态切换的时候,需要访问相关的应用数据。比如一个会议室的申请流程,如何实现状态机与应用数据的交互?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月28日 09:30 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
to banq,
是通过事件或参数来向实例化后的状态机传递相关数据吗?如果是,还请banq老师详加指点一二。谢谢

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月28日 10:12 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
你的问题是一种实际中经常遇到的解耦问题:

本帖开始时的代码transition中一段代码
State currentState = readCurrentState(taskid); //从数据库获得当前状态

实际就是和业务逻辑发生关系,通过依赖或关联关系可和业务逻辑层进行交互,当然,之间最好是面向接口的。

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月28日 21:40 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
通过banq老师的多次指点,今天终于在前面介绍的思想和部分代码的基础上实现了一个非常简单的工作流状态机,是一个有关会议室申请的流程,在这里非常感谢banq老师和其他的jdon道友。但同时也对前面一个道友谈到的观点深有体会,就是状态太多了。(我设计了一个State,下面有许多具体子类,比如Committed,Passed,Finished之类),如下图。

有没有更好的方法?另外,比如公文发文流程,它一般有拟稿、核稿、批准、归档等状态,这些状态是不是又要重写?还是可以复用会议室申请的那个工作流状态机?也就是说可不可以抽象出一个一般工作流状态机,然后根据具体应用逻辑,在实例化成会议室申请状态机、公文发文状态机、物品领用状态机之类的?

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月28日 21:44 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
这里特别想请教一下的是,对公文发文流程中的会签操作,该怎么处理?感觉很难啊,敬请指教!

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月29日 09:41 回复
banq 发表文章: 8433/ 注册时间: 2002年08月03日 17:08
状态机可以细化,分类成很多。

图画得不错,如果使用UNML的状态图就不感觉那么“飞舞”了

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2005年12月29日 15:06 回复
chenyongguang 发表文章: 11/ 注册时间: 2005年12月26日 09:44
to banq,
我用的是visio的数据流状态图,本来是准备用visio里面的UML状态图的。还请banq兄不要避重就轻,对我的疑问要详加解释才是啊,哈哈!

 

Re: 从工作流状态机实践中总结状态模式使用心得 发表: 2006年04月28日 11:43 回复
tuzhihai 发表文章: 1/ 注册时间: 2006年04月28日 11:38
但对于下列情况该如何描述:
假设一个对象处于正在执行状态,其要做的事是与另一个系统通信,这个通信可能成功也可能失败,如果成功,该对象应处于完成状态,如果不成功,该对象应该处于失败状态。
这种情况如果用单一的FROM_STATE--EVENT--TO_STATE该如何表达?

 

时间: 2024-07-28 19:43:21

从工作流状态机实践中总结状态模式使用心得的相关文章

详解C++设计模式编程中对状态模式的运用_C 语言

状态模式:当一个对象的内在状态发生变化时,允许改变其行为,这个对象看来像是改变了其类. 状态模式与策略模式的UML图几乎一模一样,下面列举了两者的不同: (1)可以通过环境类状态的个数来决定是使用策略模式还是状态模式. (2)策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类:而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系. (3)使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时

解析C++编程中如何使用设计模式中的状态模式结构_C 语言

作用:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. UML图如下: State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为. ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为. Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把

Java中的状态模式实例教程

原文链接 作者:Pankaj Kumar 译者:f0tlo <1357654289@qq.com> 状态模式是一种行为设计模式.适用于当对象的内在状态改变它自身的行为时. 如果想基于对象的状态来改变自身的行为,通常利用对象的状态变量及if-else条件子句来扮演针对对象的不同行为.状态模式Context(环境)和State(状态)分离的方式既保证状态与行为的联动变化,又使得这种变化是条理明晰且松耦合的. Context是包含了状态引用的类,此引用指向一个状态的具体实现.并且帮助把对状态的请求委

实例讲解C++设计模式编程中State状态模式的运用场景_C 语言

State模式允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类. 在面向对象系统的开发和设计过程,经常会遇到一种情况就是需求变更(Requirement Changing),经常我们做好的一个设计.实现了一个系统原型,咱们的客户又会有了新的需求.我们又因此不得不去修改已有的设计,最常见就是解决方案就是给已经设计.实现好的类添加新的方法去实现客户新的需求,这样就陷入了设计变更的梦魇:不停地打补丁,其带来的后果就是设计根本就不可能封闭.编译永远都是整个系统代码. 访问者模式则提

详解PHP中的状态模式编程_php技巧

定义状态模式,又称状态对象模式(Pattern of Objects for State),状态模式就是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改变其行为.这个对象看上去就像是改变了它的类一样 UML图 状态模式中主要角色抽象状态角色(State):定义一个接口或抽象类State,用以封装环境对象的一个特定的状态所对应的行为 具体状态(ConcreteState)角色:每一个状态类都实现了环境(Context)的一个状态所对应的行为 环境(Context)角色:定义客户端所感兴

状态模式在领域驱动设计中的使用

原文链接  作者: Tomasz Nurkiewicz  译者:陈振阳 领域驱动设计是软件开发的一种方式,问题复杂的地方通过将具体实现和一个不断改进的核心业务概念的模型连接解决.这个概念是Eric Evans提出的,http://www.domaindrivendesign.org/这个网站来促进领域驱动设计的使用.关于领域驱动设计的定义,http://dddcommunity.org/resources/ddd_terms/,这个网站有很多的描述,DDD是一种软件开发的方式: 对于大多数的软件

详解state状态模式及在C++设计模式编程中的使用实例_C 语言

每个人.事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State).最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过.在出口处也是验票,如果正确你就可以 ok,否则就不让你通过(如果你动作野蛮,或许会有报警(Alarm),:)). 有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移). 通常我们在实现这类系统会使用到很多的 Switch/Case 语句,Case 某种状态,发生什么动作,C

设计模式 ( 十七) 状态模式State(对象行为型)

设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ellse语句来做状态判断来进行不同情况的处理.但是对复杂状态的判断就显得"力不从心了".随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱.维护也会很麻烦.那么我就考虑只修改自身状态的

设计模式:状态模式(State)

允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类. 状态模式的角色 1. 环境角色Context):也称上下文,定义客户端所感兴趣的接口,并且保留一个具体状态类的实例.这个具体状态类的实例给出此环境对象的现有状态. 2. 抽象状态角色(State):定义一个接口,用以封装环境对象的一个特定的状态所对应的行为. 3. 具体状态角色(ConcreteState):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为. 案例 以酒店订房为例,房间的状态有:空闲.预