PageController(页面控制器)
上下文
决定使用MVC来动态创建应用程序时,需要关注把用户界面、控制器和业务逻辑分开。
问题:
如何创建比较复杂的应用程序的控制器?
影响因数:
MVC主要关注业务模型与视图的分开,不太关注控制器。对于胖客户而言,控制器与视图分开的需求不是很明显。但在瘦客户端,控制器与视图本身就是分开的。由于视图是在客户端的浏览上显示,而控制器是在服务器端执行。
由于不同用户的操作,会产生多个操作,共用一个视图。如果控制器和视图不分离,视图代码会重复出现。例如产品查询系统,产品查询、分页会使用同一个视图,但执行不同的操作。
Web系统中不同的控制器和视图在加载时会经理身份验证、授权、字符串查询等一系列操作,这些操作会使代码重复。
脚本服务其端页面容易创建,但显示逻辑和业务逻辑混在一块,随着复杂程度的增加,难于维护、扩展和重用。
从测试角度看,测试视图比较困难,但测试业务逻辑比较容易且重用度高,因此如果把视图与业务逻辑、控制器分开,将容易进行测试。
解决方案
使用PageController接收用户输入,并根据输入调用业务逻辑的操作,并返回结果,最后确定使用哪个视图。这一模式使业务逻辑与视图逻辑进行分离(如下图)。通常的做法是,把公用的业务逻辑写成一个基类,PageController继承这个基类,做到易于测试和一致性。
页面控制器接收用户的输入并提取输入的数据,同时调用模型操作,把模型操作的结果在视图上呈现。视图根据模型检索的数据进行显示。页面控制器把模型与视图进行分离,页面控制器承担了Http请求的任务,如会话管理、缓存、认证等任务。因此我们可以为每个链接创建一个控制器,每个控制器执行一个操作,这将变的简单。
为每个页面创建一个控制器,将导致大量代码的重复,因此需要把这些公共的操作封装到一个基类控制器中,每个页面控制器都继承该基类,也可以使用一些帮助类。
如果页面相似度越高,越复杂,继承度越深。如产品查询系统有些需要显示产品列表有些需要输入数据,我们会制作两个基类一个是ListController和DataEntryController,页面控制继承他们。这样随着系统的复杂度增高,变的不容易维护,因此开发人员人为PageController是不好的设计,而FrontController是好的设计,其实这是由于对PageController缺乏完善的支持。
大多数框架都支持PageController,如ASP、JSP、PHP等。这些框架大多数把控制器和View在服务器进行了混合,很难把进行分离。而ASP.NET对该模式进行了分离,把控制器放到一个独立类中,使用了代码隐藏技术。
变体
大多数情况下,页面控制器依赖于Http请求,如请求域、请求头部、表单域、字符串查询等信息,因此可以把与Http请求有关的操作封装到一个类中,把与Http有关的逻辑和与Http无关的逻辑进行了分离,见下图:
这样做的缺点是,增加系统开销,好处是,容易维护和开发,尤其是多人开发的团队尤为重要。
PageController的优点:
1、 简单,每个链接或请求,对应一个控制器,容易创建和维护,理解起来也容易。
2、 内置框架,所有框架都默认实现了该模式。
3、 容易扩展,可以使用一些帮助类进行扩展。
4、 职责分离
5、 重用度高
PageController的缺点:
1、每个页面一个控制器。由于简单,只能在导航比较简单的Web系统中使用该模式,如果系统比较复杂建议使用Front Controller.
2、继承深度比较大:由于该模式采用继承的方式实现重用,如果业务比较复杂,继承深度比较大,不易维护。
3、依赖Web框架
在 ASP.NET 中实现 Page Controller
在Asp.NET中构建Web应用程序,利用内置的PageController框架,同时利用ASP.NET的事件驱动特性
实现策略
在默认情况下Asp.net页面框架实现了PageController的各种概念。Asp.NET 页面框架自动实现了,在客户端感知用户操作,把用户请求发送到服务器端,服务器端自动调用合适的方法进行响应。这个过程对开发人员来说是不可知的。页面框架可以进行扩展,页面框架的各种事件都是对外开放的。
页面生命周期
- 下面按发生顺序列出了页面生命周期中最常见的各个阶段。其中还包括引发的特定事件,以及处理请求时在各个阶段可能执行的一些典型操作:
- ASP.NET 页面框架初始化(事件: Init )。这是生命周期的第一个步骤,该步骤将初始化 ASP.NET 运行库以便为响应请求做好准备。
- 用户代码初始化(事件: Load )。您应该执行与应用程序具体相关的常见任务,例如,当页面控制器引发 Load 事件时打开数据库连接。您可以假设:引发 Load 事件后,服务器控件已创建并完成初始化、状态已还原并且窗体控件反映了客户端的更改。 [Reilly02]
- 与应用程序相关的事件处理。在此阶段,您应该执行与应用程序相关的处理,以响应控制器引发的事件。 .
- 清理(事件: Unload )。 该页面已完成生成,现在可以丢弃。您应该关闭 Load 事件打开的任何数据库连接,丢弃任何不再需要的对象。在连接对象被作为垃圾回收后,Microsoft.NET Framework 将自动关闭数据库连接。不过,您对何时进行垃圾回收没有任何控制权。因此,显式关闭数据库连接以充分利用数据库连接池是一个很好的做法。
注意:还有几个页面处理阶段没有在这里列出。不过,这些阶段不用于大多数页面处理情况。
实现方式