WF4.0实战(二十二):一个实际生活中状态机的例子

    这是实际生活中的一个状态机的例子:“门”。这个例子是Mebyon Kernow写的,使用状态机对门的状态进行控制,我觉得是学习WF4.0中状态机的好例子。所以,简单的翻译了他这篇文章。希望对你学习WF4.0的状态机有所帮助。以下是正文。原文是:A practical State Machine example

------------------------------------------------------正文分割线------------------------------------------------------------

    现在,状态机的活动已经发布在Codeplex上面了(详见:http://wf.codeplex.com/)。我找出一个经常使用的例子。用这个例子用来演示如何使用状态机来控制一个建筑物的进出(控制建筑物的门),以及展示一下状态机活动包中的主要功能。

    这个例子的模型是对门的控制。宿主是一个WPF应用程序(WPF可以实现一些比较好的样式!)。想象一下,如果你去'开启'一个建筑物。此时,每一个门都是关闭或者锁住的,这形成这个例子中状态机初始化的状态。因此,在状态机中我们需要等待一个事件触发来对门解锁,现实中可能要使用密码键盘或智能卡去开锁。这个例子的状态机的State和Transition如下图所示:

WF4.0实战(二十二):一个实际生活中状态机的例子

    当用户提供自己的凭据来触发上图中的名字为Unlocked的Transition-这里你可能需要你传递PIN号码(或者卡上的号码)给Transition ,然后再查询数据库进行验证。这个例子中为了简单起见,没有任何验证。

    从ClosedLocked状态出来,会存在三种可能性。一种是用户打开门;一种是因为一些原因又锁上门;一种是可能是他没有打开门,一段时间过后自动锁上门,回到ClosedLocked状态。在这个状态上有三个Transition被执行,其中两个是等到事件发生,第三种是用了一个Delay 活动,时间设置为5秒钟。下面,我将会对State 和Transition进行解释,它们对理解状态机如何运作是至关重要的。

States & Transitions

    一个状态机由State的集合和State之间的transition集合构成的。必须有一个初始状态,可以选择是否有一个结束的状态(完成了工作流实例执行),当你进入一个State,你可以执行一些活动,同样在退出一个State,的时候,还可以执行一些活动。要定义的Entry和Exit的活动,双击State ,设计将显示如下所示...

2 

     执行State活动的时候先执行Entry活动(它可以是一个Sequence ,这样你就可以运行多个活动了)。接着是去寻找出这个State所有的Transition。然后,执行每个Transition,这在我的状态机例子中在活动执行中创建了书签,然后等待恢复这个书签。这似乎有些奇怪。如果你有3个Transition,我们就要要为这三个分支设计触发的活动。其实状态机的引擎只需等待这些活动的一个完成,谁先谁执行,只要一个触发完成,这样Transition就完成了。

3

    理解Transition最简单的方式是把它当作一个Pick活动看待。当状态机处在一个给定的状态,我们去执行每一个Transition的方式类似于Pick活动处理方式,其中一个活动最先完成,Transition就完成。你可以在Transition上设置一些条件。这个时候,只有事件触发而且条件返回为True的时候,Transition才会完成。在ClosedLocked状态中,Transition类似于下图所示:

4

    上图中,我省略了Action,Transition分支和Pick活动稍微不同的地方。当Transition的触发活动完成后,如果有创建Action活动,也将会执行Action活动(假设条件返回为true)。

    所以,在State中我们正等待一个活动的完成。在上图中有一个自定义活动:DoorUnlocked和DoorLocked活动。他们都是DoorEvent活动的实例,是由客户端恢复的书签。这项活动的代码是相当简单,如下...

public class DoorEvent : NativeActivity
{
    protected override bool CanInduceIdle
    {
        get { return true; }
    }

    [RequiredArgument]
    public InArgument<string> BookmarkName { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        context.CreateBookmark(BookmarkName.Get(context), new BookmarkCallback(OnBookmarkResumed));
    }

    private void OnBookmarkResumed(NativeActivityContext context, Bookmark bookmark, object state)
    {
    }
}

    它有一个必要的BookmarkName属性,用来定义的书签的名称。在这个例子中,书签的名称和Transition的标签相同。

在WPF中宿主

    这个例子中宿主在WPF中,并使用MVVM模式(这里只有一个View模型)来维护用户界面。该用户界面需要知道现在状态机是什么状态,这样才能恢复正确的书签,因此在每一个State的活动中我使用另外一个自定义活动将状态机的状态传递出来供View模型使用。这个状态将更新的View模型上的属性。

    在这个例子中,在视图模型中使用了一些类 - MainViewModel类是应用程序的父类,它包含一组DoorModel实例,其中的一个表示UI上的一道门。当DoorModel创建时,将创建WorkflowApplication实例,并添加一个扩展到WorkflowApplication实例中(IDoor),这个扩展由DoorModel实现。当通知模型状态改变时,正是这种扩展,工作流将回到DoorModel中。该系统的伪代码如下所示...

1、DoorModel实例被创建。设置它的名称(显示在屏幕上)。
2、在一个WorkflowApplication实例中创建一个状态机实例。
3、状态机启动,并立即执行的第一个State活动'ClosedLocked'。这将先执行SetDoorState活动,并调用IDoor.SetState(),并给方法传递一个枚举值来说明状态机是什么状态 。
4、根据工作流的状态,使用enable/disable命令更新用户界面。

此后,用户界面负责管理状态机。当一个命令,如'Unlock’被执行的,它只要在状态机中恢复正确的书签...

_unlockCommand = new DelegateCommand((unused) =>
    {
        _stateMachine.ResumeBookmark("Unlocked", null);
    },
    (unused) =>
    {
        return this.DoorState == DoorState.ClosedLocked;
    });

    在这里我使用的一个DelegateCommand类,这样我可以使用lambda函数响应命令了。

    当用户点击用户界面,执行相应的命令来恢复书签,完成流程中的一个Transition。进入一个新的状态,回调到视图模型,产生新的状态。

    我还想说明的这个应用程序的例外一部分。在每一个Transition中使用了自定义的PlaySound活动。当Transition完成的时候,发出简单的声音来表示门的状态。这个例子中,当超时的时候,发出一种声音。另外一种是门被锁上的时候发出另外一种声音。最初,我将声音定义在State活动的‘Entry’ 元素上面。这不能说明到底是哪一个transition是刚刚触发的。即有没有办法区分状态机是从哪一个Transition进入ClosedLocked中的,也就不能播放不同的声音进入该活动了。

当您运行该应用程序你会看到下面的UI ...

5

    如果你点击Unlock 按钮,就会执行工作流,改变状态为ClosedUnlocked。你还会看到一组对应于现在有效状态((Lock和Open)按钮显示 如果你解锁一道门,然后离开约5秒钟,门会自动再次锁上。对于所有的Transition,您可以听到一些声音 - 如果你听不到声音,检查每一个Transition的PlaySound活动和选择自己的文件播放(我在Windows 7文件目录可能会和你的机子不同)。

代码:http://files.cnblogs.com/zhuqil/Doors.rar


原文链接: http://www.cnblogs.com/zhuqil/archive/2010/06/26/a-practical-state-machine-example.html

你可能感兴趣的:(WF4.0实战(二十二):一个实际生活中状态机的例子)