设计模式 学习笔记 之十一
第14章 State模式
我们在开发软件时有时会遇到有限状态机(FSM),我们需要使用高效的方式来实现有限状态机。但是,在更多的时候,我们却忽视了有限状态机,忘记了使用FSM来对复杂的软件行为进行建模。实际上,FSM是软件宝库中最有用的抽象之一,它们提供了一个简单、优雅的方法去提示和定义复杂系统的行为。FSM同样也提供了一个易于理解、易于修改的有效实现策略。本章中学习的State模式正是研究如何优雅高效地实现FSM。按照惯例,我们仍然通过一个例子来学习。
例子:地铁闸机FSM
地铁闸机存在一个简单的FSM,它的状态转移图(STD)可以被表示如下:
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的源文件。 |
这样子生成的类图可以用下图来表示。
第5步,创建一个FSM类,它继承由SMC生成的Context类并实现在第2步中定义的抽象接口。这个FSM类的作用就是实现FSM中的动作。在我们这个例子中,我们定义一个名为TurnstileFSM的类。对于开发人员而言,真正需要关注的就是如何去实现TurnstileFSM这个类了。
第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来建模。下面的状态转移图表示一个应用程序的用户登录部分。
许多通信协议中也大量应用了FSM,例如,下图表示了一个应用程序发送消息的有限状态机。
小结
有限状态机是一种非常强大的编程工具,但是开发人员对它的应用还并不充分。在许多情况下,使用它们都会有助于创建更清楚、更简单、更灵活以及更准确的代码。使用SMC程序创建FSM给我们带来了巨大的便利,让我们更容易地创建出State模式的高质量实现。