解决这个问题的一个方法是将Web应用程序作为有限状态机来创建。让我们来看一个实际的、可重用的Java类,使用它能够以一个有限状态机的模式创建一个健壮的Web应用程序。
到底什么是有限状态机,它又是怎样帮助你开发你个健壮的Web应用程序的?要寻找这些问题的答案,以及想弄清怎样为一个Web应用程序建立状态表,请看“将Web应用程序建立成有限状态机”这篇文章。
为一个应用程序建立状态表是很耗费时间的,但是一旦拥有了状态表,为程序编码就很容易了。为了简化状态表的创建,我设计了一个泛型类来实现有限状态机的细节,见列表A。类Dispatcher被设计成和Java servlets一起使用,但是其概念和方法在其它编程语言和Web程序中同样可以使用。
Dispatcher是一个有限状态机,它根据程序的状态将请求按照ID分派给由状态表映射的方法。Dispatcher在使用HTTP session在请求之间维持状态机的状态,只要你愿意,可以很容易地将这种sesion管理机制扩展到程序数据的维持上。
使用Dispatcher类是很简单的。首先,通过传递一个servlet对象的引用来创建一个Dispatcher实例——最好在servlet的init方法内这样做。然后用Web程序的状态表来配置Dispatcher实例,方法是对状态表的每一项重复调用mapRequest方法。将请求ID、状态标识符和方法的名字作为mapRequest的参数。图A描述了Dispatcher的全部编程接口。
图A
方法 | 描述 |
Dispatcher(HttpServlet,Class) | 通过给定的servlet实例化一个新的Dispatcher对象,session的信息存放在session类Class中 |
Dispatcher(HttpServlet) | 通过给定的servlet实例化一个新的Dispatcher对象,不存储任何特定的程序session信息 |
dispatch(HttpServletRequest,HttpServletResponse) | 将给定的HTTP请求分派给对应于有限状态机当前状态和状态标定义的相应的servlet方法 |
mapRequest(String,String,String) | 将一个请求ID映射到相应状态指定的方法 |
Dispatcher类的编程接口
对于某一个状态和请求而调用的方法是作为一个字符串用名字来区分的,而且必须是servlet类的成员之一。包含状态机状态、请求ID和HTTP请求及响应的Dispatcher.Session类被作为参数接收。该方法对于处理请求和设置状态机新状态进行响应。图B列出了Dispatcher.Session类的编程接口。
图B
方法 | 描述 |
getReqId | 返回请求标识 |
getRequest | 返回与正在处理的HTTP请求相应的HttpServletRequest对象 |
getResponse | 返回与正在处理的HTTP请求相应的HttpServletResponse对象 |
getState | 返回有限状态机的状态 |
setReqId | 设置供Dispatcher类内部使用的请求标识 |
setRequest | 设置供Dispatcher类内部使用的HttpServletRequest对象 |
setResponse | 设置供Dispatcher类内部使用的HttpServletResponse对象 |
setState | 设置有限状态机的状态,只能被Dispatched调用以更新状态机状态 |
Dispatcher.Session类的编程接口
Dispatcher类使用一个内部Dispatcher.Session类的实例在请求之间保持状态机的状态。当程序收到第一个请求时,Dispatcher创建一个Dispatcher.Session对象实例并把它保存在一个HttpSession对象中。当以后再接收到请求时,Dispatcher从现存的session中取回Dispatcher.Session对象实例。
大多数的动作都在_dispatch方法内进行,见列表B。在从session中取回以前的状态后,_dispatch试图找到一个与请求ID和状态匹配的方法名称。一旦找到了相应的方法,Dispatcher就激活它的映像。
Dispatcher希望从称为_reqID的参数中获得请求ID。因此,每个向servlet提出的请求必须包含_reqID参数,成为URL的一部分(使用GET请求的情况),或者作为一个隐藏参数(使用POST请求的情况)。这样,Dispatcher就能够区分请求并从状态表中获得要调用的方法。如果一个HTTP请求没有提供_reqID参数,请求的标识就是null,对应于状态标里的默认事件。于是可以使用相似的但不包含任何参数的URL来访问程序的初始页面。