设计模式 学习笔记 之十一

第14章 State模式

我们在开发软件时有时会遇到有限状态机(FSM),我们需要使用高效的方式来实现有限状态机。但是,在更多的时候,我们却忽视了有限状态机,忘记了使用FSM来对复杂的软件行为进行建模。实际上,FSM是软件宝库中最有用的抽象之一,它们提供了一个简单、优雅的方法去提示和定义复杂系统的行为。FSM同样也提供了一个易于理解、易于修改的有效实现策略。本章中学习的State模式正是研究如何优雅高效地实现FSM。按照惯例,我们仍然通过一个例子来学习。

例子:地铁闸机FSM

地铁闸机存在一个简单的FSM,它的状态转移图(STD)可以被表示如下:

clip_image002

FSM也可以用一个状态转移表(STT)来表示。下面是地铁闸机FSM的STT。

初态

事件

末态

动作

Locked

coin

Unlocked

unlock

pass

Locked

alarm

Unlocked

pass

Locked

lock

coin

Unlocked

thankyou

有了对FSM的表示,我们来看一下怎样优雅地实现一个FSM。我们发现,在FSM中,包含三个要素:状态、事件和动作。其中,状态要素和事件要素代表了FSM的逻辑,而动作要素则与FSM无关。作为开发人员,我们真正关注的是动作要素,即当状态发生变化之后要执行的操作。而FSM的逻辑我们则希望尽量少关注,最好是不关注。Martin Robert(Bob大叔)对这个问题给出了一种优雅的实现方案,下面我们以地铁闸机FSM为例子来看看怎么一步步地完成这个方案。

第1步,到SourceForge网站上下载SMC(State Machine Compiler)程序。SMC是Bob大叔开发的代码生成工具,利用它我们可以很容易地生成FSM的代码。

第2步,根据状态转移表的“动作”这一列来创建一个抽象接口,这个抽象接口中包括了“动作”列中的所有方法。在我们这个例子中,我们把这个抽象接口命名为Turnstile,下面是分别使用C++和Java来定义这个接口的内容。

C++

class Turnstile

{

public:

    virtual void unlock() = 0;

    virtual void alarm() = 0;

    virtual void lock() = 0;

    virtual void thankyou() = 0;

};

Java

public interface Turnstile

{

    public void unlock();

    public void alarm();

    public void lock();

    public void thankyou();

}

第3步,SMC程序需要你按照一定的语法编写一个.sm文件,在这个.sm文件中描述你的FSM。这里要注意的是.sm文件的文件名和文件中%class所定义的类名应该就是第2步中定义的抽象接口的名字。我们下面给出Turnstile.sm文件,通过这个文件来学习.sm文件的语法。

 

/* Turnstile.sm */

/* SMC supports java/C++ comment. */

 

/* Define class name. Class name must be the same as the .sm filename and package name. */

%class Turnstile

/* Define package name. For C++ code, this is namespace name. */

%package turnstile

/* For C++ code, header filename must be specified. */

%header Turnstile.h

 

/* Define start state. */

%start MainMap::Locked

/* Define the state-transition table. */

%map MainMap

%%

Locked

{

    coin Unlocked {unlock();}

    pass Locked {alarm();}

}

Unlocked

{

    pass Locked {lock();}

    coin Unlocked {thankyou();}

}

%%

第4步,使用SMC程序来生成代码。我们对生成java和C++代码分别来看。

java

执行如下的命令:

% java -jar Smc.jar -java Turnstile.sm

执行后之后,SMC会生成一个名为TurnstileContext.java的源文件。

C++

执行如下的命令:

% java -jar Smc.jar -c++ Turnstile.sm

执行这条命令之后,SMC会生成一个名为Turnstile_sm.cpp和Turnstile_sm.h的源文件。

这样子生成的类图可以用下图来表示。

clip_image004

第5步,创建一个FSM类,它继承由SMC生成的Context类并实现在第2步中定义的抽象接口。这个FSM类的作用就是实现FSM中的动作。在我们这个例子中,我们定义一个名为TurnstileFSM的类。对于开发人员而言,真正需要关注的就是如何去实现TurnstileFSM这个类了。

clip_image006

第6步,把相应的库加入到编译过程中。下面是对C++代码和Java代码的不同做法。

C++

把/lib/C++/statemap.h加入到代码仓库中

Java

把/lib/Java/statemap.jar加入到CLASSPATH环境变量中

第7步,创建出TurnstileFSM的实例,并通过TurnstileContext中定义的事件方法与这个实例交互,使其发生状态变化。

以上就是使用SMC实现State模式的全部步骤。使用SMC使开发人员仅关注于状态机的动作,而把FSM的逻辑全部交给SMC,这就大大提高了开发人员的效率,这个实现方案是很优雅的。

有限状态机的应用

有限状态机可以用于对复杂系统的行为建模。GUI程序往往具有复杂的行为逻辑,因此GUI程序常常可以用FSM来建模。下面的状态转移图表示一个应用程序的用户登录部分。

clip_image008

许多通信协议中也大量应用了FSM,例如,下图表示了一个应用程序发送消息的有限状态机。

clip_image010

小结

有限状态机是一种非常强大的编程工具,但是开发人员对它的应用还并不充分。在许多情况下,使用它们都会有助于创建更清楚、更简单、更灵活以及更准确的代码。使用SMC程序创建FSM给我们带来了巨大的便利,让我们更容易地创建出State模式的高质量实现。

posted @ 2011-05-15 18:42  李嘉 (Justin)  阅读(3227)  评论(3编辑  收藏  举报