Page Controller(页面控制器)
|
更新日期: 4/1/2004
Page Controller(页面控制器)
版本: 1.0.1
本页内容
|
上下文 |
|
问题 |
|
影响因素 |
|
解决方案 |
|
示例 |
|
结果上下文 |
|
测试考虑事项 |
|
相关模式 |
|
致谢 |
上下文
您已经决定使用
Model-View-Controller
(MVC)
模式来将动态 Web 应用程序的用户界面组件与业务逻辑分隔开来。要构建的应用程序将以动态方式构造网页,但网页间导航多为静态导航。
影响因素
以下因素影响这种情况中的系统,在考虑上述问题解决方案时必须协调这些因素:
• |
|
• |
在动态 Web 应用程序中,多用户操作可以导致不同的控制器逻辑,然后显示相同页面。例如,在基于 Web 的简单电子邮件应用程序中,发送邮件和从收件箱中删除邮件这两个操作都可能将用户返回(刷新后的)收件箱页面。虽然这两种活动完成之后显示相同页面,但应用程序必须根据上一页面以及用户所单击的按钮来执行不同的操作。 |
• |
显示大多数动态网页的代码都包括非常相似的步骤:验证用户身份、从查询字符串或表单域中提取页面参数、收集会话信息、从数据源检索数据、生成页面动态部分以及添加适用的页眉和页脚。这可能导致大量的代码重复。 |
• |
脚本化服务器页面(例如 ASP.NET)可能很容易创建,但在应用程序不断增大时可能带来一些缺点。脚本化页面不能较好地分隔控制器和视图,因而降低了重用的可能性。例如,如果多个操作将导致相同页面,在多个控制器之间重用显示代码则会比较困难,这是因为显示代码与控制器代码混合在一起。对散布于业务逻辑和显示逻辑之间的脚本化服务器页面也更加难以进行测试和调试。最后,开发脚本化服务器页面要求同时精通开发业务逻辑和制作美观高效的 HTML 页面,而很少有人兼备这两项技能。基于上述考虑,因此有必要最大程度地减少脚本化服务器页面代码,而在实际类中开发业务逻辑。 |
• |
正如 |
• |
通用外观和导航结构往往可以提高 Web 应用程序的可用性和品牌认知度。但通用外观可能会导致显示代码重复,特别是在脚本化服务器页面中嵌入代码时。因此,需要一种机制以提高页面间显示逻辑的重用性。 |
解决方案
使用
Page Controller
模式接受来自页面请求的输入、调用请求对模型执行的操作以及确定应用于结果页面的正确视图。分隔调度逻辑和所有视图相关代码。如果合适,创建用于所有页面控制器的公用基类,以避免代码重复并提高一致性和可测试性。图 1 显示了页面控制器与模型和视图的关系。
图 1:
页面控制器结构
页面控制器可接收页面请求、提取所有相关数据、调用对模型的所有更新以及向视图转发请求。而视图又将根据该模型检索要显示的数据。定义独立页面控制器将分隔模型与 Web 请求细节(例如会话管理,或使用查询字符串或隐藏表单域向页面传递参数)。按照这种基本形式,为 Web 应用程序中的每个链接创建控制器。控制器因而将变得非常简单,因为每次仅须考虑一个操作。
为每个网页(或操作)创建独立控制器可能会导致大量代码重复。因此应该创建
BaseController
类以合并验证参数(请参阅图 2)等公用函数。每个独立页面控制器都可以从
BaseController
继承此公用功能。除了从公用基类继承之外,还可以定义一组帮助器类,控制器可以调用这些类来执行公用功能。
图 2:
使用
BaseController
消除代码重复
如果多数页面相似,并且可以将公用功能放入一个基类,则此方法非常有效。页面变化越多,必须插入继承树的级别也就越多。比如,所有页面都分析参数,但只有显示列表的页面才从数据库检索数据,而需要输入数据的页面则会更新模型而不检索数据。现在可以引入两个新基类,即
ListController
和
DataEntryController
,这两个类都是继承
BaseController
而得到的。然后列表页可以从
ListController
继承,而数据输入页则可以从
DataEntryController
继承。虽然这种方法在这个简单示例中非常有效,但在处理实际业务应用时,继承树可能很深且非常复杂。您可能希望向基类中添加条件逻辑,以适应某些变体,但如此操作将违反封装原则,基类也会因此在更改系统时造成较大麻烦。因此在应用程序变得更为复杂时,应当考虑使用帮助器类或者
Front Controller
模式。
因为很多时候都需要对 Web 应用程序使用页面控制器,因此多数 Web 应用程序框架都默认实现页面控制器。大多数框架以服务器页面的形式包含了页面控制器(例如 ASP、JSP 和 PHP)。服务器页面实际上组合了视图和控制器的功能,但没有提供显示代码与控制器代码之间的相应分隔。遗憾的是,对于有些框架,混合视图相关代码与控制器相关代码很轻松,但要正确分隔控制器逻辑却很困难。因此,
Page
Controller 方式在很多开发人员中口碑不佳。现在很多开发人员都将
Page Controller
与较差设计联系在一起,而将
Front Controller
与较好设计联系在一起。实际上,这种感觉是由于具体的实现在不完善的情况下造成的;
Page Controller
和
Front Controller
都是可行性极佳的体系结构选择。
因此,最好将控制器逻辑单独放入可以从服务器页面调用的独立类。ASP.NET 页面框架提供了可以实现这种分隔的完善机制,这种机制称为”代码隐藏类”。(请参阅
在
ASP.NET
中实现
Page Controller
)。
变体
大多数情况下,页面控制器取决于基于 HTTP 的 Web 请求的具体细节。因此,页面控制器代码通常包含对 HTTP 头、查询字符串、表单域、多部分表单请求等的引用。因此在 Web 应用程序框架之外测试控制器代码非常困难。唯一方法是通过模拟 HTTP 请求和分析结果来测试控制器。这种类型的测试既费时且易出错。因此,要提高可测试性,可以将依赖 Web 的代码和不依赖 Web 的代码分别放入两个单独类中(请参阅图 3)。
图 3:
分隔依赖
Web
的代码和不依赖
Web
的代码
在此示例中,
AspNetController
封装了在应用程序框架 (ASP.NET) 上的所有依赖项。例如,它可以提取来自 Web 请求的所有传入参数,并使用独立于 Web 界面的方式(例如,使用集合)将其传递至
BaseController
。此方法不仅可提高可测试性,而且允许通过其他用户界面重用该控制器代码,例如胖客户端界面或自定义脚本语言。
此方法的缺点在于增加了开销。现在新增了一个类,并且在处理每个请求前必须首先对其进行转换。因此,应尽可能控制器受环境影响的部分,并权衡选择降低依赖性与提高开发效率及执行效率。
结果上下文
使用
Page Controller
模式存在下列优缺点。
优点
• |
|
• |
|
• |
|
• |
|
• |
|
缺点
由于其简单性,
Page Controller
是大多数动态 Web 应用程序的默认实现方式。但是应该了解下列限制:
• |
|
• |
|
• |
|
测试考虑事项
因为
Page Controller
依赖于 Web 应用程序框架的具体细节(例如,查询字符串和 HTTP 头),因此不能在 Web 框架之外对控制器类进行实例化和测试操作。如果需要对控制器类运行一组自动单元测试,则每次测试时都必须启动 Web 应用程序。然后必须使用可执行所需功能的格式提交 HTTP 请求。此配置为测试带来许多依赖性和不良影响。要提高可测试性,请考虑将业务逻辑(包括变得更加复杂的控制器逻辑)与依赖 Web 的代码分隔开来。
相关模式
有关详细信息,请参阅以下相关模式:
• |
|
• |
|
• |
|