那么控制器管理的这个 "流(flow)" 是什么呢?从页面加载、等候填写的表单返回角度来讲,典型的 Web 应用程序由定义良好的与用户进行交互的序列组成。在这种情况下,Web 应用程序就像是一个事件驱动的状态机(state machine)。这个事件模型就是典型的 MVC 架构通过控制器实现的模型。
例如,假设用户向服务器请求某个页面,页面中包含要填充的表单。用户花了些时间思考,填充答案,然后提交表单。当这个事件到达服务器(控制器模块)时,根据当前状态、用户提交的数据和业务逻辑,把应用程序移动到下一个逻辑状态。这种状态转换的结果,正如用户所看到的那样,是按顺序排列的或早期页面(和错误信息)的下一页的显示。
当状态机在从开始状态到结束状态的途中推进时,就重复这个循环,在某一点上,Web 应用程序被认为是实现了特定用例要求的功能。状态图控制着从 "开始" 状态到 "结束" 状态的各种可能的数据流,它既可以由控制器模块(通常是 servlet)显式实现的,也可以像在某些 Web 开发框架中那样,被外部化为配置文件中的元数据。
不论框架是如何实现的,与状态机的基本思想总是一致。在开发基于这个模型的 Web 应用程序时,就会出现大量问题,如下所述:
根据状态机的尺寸和维护客户当前状态所需要的数据量(因为一个 Web 应用程序在同一时间可能会有大量客户同时访问),应用程序逻辑可能会变得没有必要的杂乱或复杂。
在状态转换的序列中,客户单击浏览器的 Back 按钮的时间是不一定的,而且客户还可能克隆浏览器窗口,从而初始化并行的动作序列。任何一种情况都会导致已经传递的状态在原来的交互中发生多重(有时甚至是并发)提交。结果,应用程序必须跟踪每一个事务,并对每个事务提供正确的响应。
当 Web 应用程序要从跨多个页面的一系列表单中搜集用户信息时,也会出现类似的问题。如果后一个表单的生成取决于用户在前一个表单中提供的响应的组合,那么应用程序就必须跟踪在每个交互中输入的响应,并确保每一个交互都返回正确的页面。
一般来说,模型 2 Web 开发框架提供了定制技术,可以调节上述的一个或多个问题。但是,没有一个技术像基于 continuation 的方案那样直观、容易开发,基于 continuation 的方案提供了解决所有这些问题的一揽子方案。
关于事件驱动编程编程用户界面的事件驱动风格可以追溯到开始使用客户机 - 服务器架构的时候。它基于中央事件处理器以及在处理器上注册的大量事件句柄。每个句柄注册自己感兴趣的事件,当特定事件发生的时候,中央事件处理器会根据通知注册了事件的相关句柄。用户的交互状态在中央模块中维护,根据内部持有的当前状态,中央模块把引入的事件分派给注册的句柄。
大多数基于 Web 的互交是事件驱动编程的特例,在这里,界面显示被委托给 Web 浏览器,而不是由运行在用户工作站上的胖客户端可执行程序管理。虽然典型的胖客户端不允许用户驱动的功能(例如后向导航和克隆),但是 Web 浏览器支持甚至鼓励这类功能。当然,资源丰富的程序员已经找到了定制浏览器界面的方法(使用脚本代码),禁止这类操作,但是这样就形成了对不同浏览器的依赖性。
虽然 MVC 的事件驱动风格的编程有许多优势,但是它还造成业务功能分布到多个模块中,从而使它变得非常复杂,难以开发、理解、维护复杂程度合理的 Web 应用程序。虽然开发了许多 Web 开发框架(例如 Struts、Spring、以及 JavaServer Faces)来隐藏大多数 MVC 风格的界面背后的复杂结构,但是有一些开发人员已经开始认识到这样的事实:其他编程模型值得深入研究。