JiveJdon Community Forums
在线388人   首页   主题表   培训咨询   标签   精华   查搜   注册    登陆 RSS
首页 » 论坛 » 设计模式、框架和架构
???en_US.forumThreadPrev.name??? 上一主题
  Go back to the topic 返回本主题   Go back to the topic listing返回主题列表
???en_US.forumThreadNext.name??? 下一主题
1 2 Go 总共有 26 回复 / 2
 发表新帖子   回复该主题贴
banq

悄悄话
发表文章: 9485
注册时间: 2002年08月03日 17:08
从工作流状态机实践中总结状态模式使用心得 2003年12月07日 00:10 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
标签列表 工作流(23)      state模式(9)      设计模式(165)     
状态模式好像是很简单的模式,正因为状态好像是个简单的对象,想复杂化实现设计模式就不是容易,误用情况很多。

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

目前在实现一个电子政务项目中,需要进行流程状态变化,在电子政务设计中,我发现,如果一开始完全按照工作流
规范开发,难度很大,它和具体项目实践结合无法把握,而且工作流规范现在有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{

//
public void run(StateOwner stateOwner){
stateOwner.setCurrentState(this);
}

//转换到下一个状态的规则
//当前是Running状态,下一个状态可能是暂停、结束或者强制退出等
//状态,但是绝对不会是Not_Started这样的状态
//转换规则在这里得到了体现。
public State next(Event e) {
if(transitions == null){
addEventState(new EventImp(
"PAUSE"), new Suspended());
addEventState(new EventImp(
"END"), new Completed());
addEventState(new EventImp(
"STOP"), new Aborted());
}
return super.next(e);
}

}



外界直接调用 StateMachine的关键方法transition;实行状态的自动转变。


public class StateMachine {

/**
* 状态切换
* 根据Event参数,运行相应的状态。
* 1. 获得当前状态
* 2. 使用当前状态的next()转换
* |Event
* ___currentState__|______newState___
*
* @param inputs
*/

public final void transition(String taskid, Event e) throws Exception {
State currentState = readCurrentState(taskid);
//从数据库获得当前状态
StateOwner stateOwner = new StateOwner(taskid, currentState);
//转换状态
currentState = currentState.next(e);
if (currentState != null) {
currentState.run(stateOwner);
saveCurrentState(stateOwner);
//保存当前状态
}
}

}

iceant

悄悄话
发表文章: 459
注册时间: 2002年10月13日 22:32
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月08日 11:16 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
我原来的想法是使用 Event 为中心的处理机制: Event + Listener

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

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

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

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

chenye99

悄悄话
发表文章: 15
注册时间: 2003年06月23日 10:20
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月08日 11:34 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
> 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方法的代码?
banq

悄悄话
发表文章: 9485
注册时间: 2002年08月03日 17:08
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月08日 18:00 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
StateT的next方法如下:


public State next(Event e) throws InvalidStateException{
logger.debug(" transitions size = " + transitions.size());
String name = e.getName();
if (transitions.containsKey(name))
return (State) transitions.get(name);
else
throw new RuntimeException(
"Input not supported for current state "
+
" state is " + this.getClass().getName()
+
" Event is " + e.getName() );
}
chenye99

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

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

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

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

banq

悄悄话
发表文章: 9485
注册时间: 2002年08月03日 17:08
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月09日 14:27 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
to wild fox
状态模式只关注某个状态,以及这个状态可能转入的下几个状态。

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

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

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

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

wild fox

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

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

悄悄话
发表文章: 9485
注册时间: 2002年08月03日 17:08
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月10日 09:18 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
to wild fox
你们存在的这个情况,说明没有真正用好状态模式,这再一次说明,模式用好真的不容易。

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

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

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

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

banq

悄悄话
发表文章: 9485
注册时间: 2002年08月03日 17:08
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月10日 09:19 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
to chenye99
状态有子状态,类似树形结构。使用状态模式处理子状态会更复杂。
天下为公

悄悄话
发表文章: 31
注册时间: 2003年07月15日 17:09
Re: 从工作流状态机实践中总结状态模式使用心得 2003年12月26日 17:40 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
EventImp与
Event的关系,请说一下,我不是太明白?
wm_creat

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

悄悄话
发表文章: 11
注册时间: 2005年12月26日 09:44
Re: 从工作流状态机实践中总结状态模式使用心得 2005年12月26日 09:47 到本帖网址 加入本帖到收藏夹 发送到手机 回复该主题
您好,请问banq,StateOwner类的源代码是什么?对StateOwner的作用不甚理解.谢谢
这个主题有 26 回复 / 2Go 1 2
???en_US.forumThreadPrev.name??? 上一主题
  Go back to the topic 返回本主题   Go back to the topic listing返回主题列表    返回页首返回页首
???en_US.forumThreadNext.name??? 下一主题
热点TAG: AOP cache 缓存 DDD EJB 集群 设计模式 Hibernate IOC JiveJdon OO RBAC Seam Spring Struts
正在读取,请等待...
google yahoo 新浪ViVi 365Key网摘 天极网摘 CSDN网摘 添加到百度搜藏 POCO网摘 博采网摘
查询本论坛内 回复超过的热门帖子
     回复该主题贴
标题
 
粗体 斜体 下划线 插入图片 插入代码 插入url链接 插入附件
内容
  发贴前查询 标签列表勿重复发表问题

RSS 手机阅读 add to google add to yahoo
解惑之道在J道 ,打造中国最具影响力的的企业软件社区
OpenSource JIVEJDON v3.0 Powered by JdonFramework Code © 2002-08 jdon.com
anti spam