ASP.NET 表示模式

发表于:2017-8-22 10:14

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Dino Esposito    来源:51Testing软件测试网采编

#
DotNet
#
dotnet
分享:
  在分层的 Web 应用程序中,表示层与导航逻辑、业务逻辑和数据访问同样重要。您应尽量将表示层(通常指表示逻辑)与 UI 技术和平台独立开来,虽然这并不容易做到。设计模式可帮助您实现此目标。
  在本月的专栏中,我将介绍一些构建 ASP.NET 表示层所用的设计模式。我首先要介绍的是模型-视图-控制器 (MVC) 模式(所有专门面向 UI 的模式的根模式),并介绍其在 ASP.NET MVC Framework 范围之外的 ASP.NET 适用性。
  深入了解 ASP.NET 事件处理程序
  ASP.NET 表示层主要由与 HTTP 运行时环境配合的 .aspx 页面组成。有时,典型 .aspx 页面可能会根据特定用户的操作(如按钮单击或列表选择)提出 HTTP 请求。在 ASP.NET Web 窗体编程过程中,这些事件通常由方法和事件处理程序依次处理,然后在页面的代码隐藏类中编写。从表面看,用户操作和系统反应好像是直接连接的,在桌面应用程序中可能是这样,但在 ASP.NET 中却并非如此。
  在 ASP.NET 中,从用户单击到显示更新页面期间发生了很多事情,无论您当前是否正在使用 AJAX,但在您使用 ASP.NET MVC Framework 构建的 ASP.NET 应用程序中,信息流更短。
  让我们看一个按钮单击事件。开发人员通过在 Button 控件的 Click 事件处理器中编写一些代码来处理此事件。这些代码存在于页面的源代码中,如下所示:
  void Button1_Click(object sender, EventArgs e)
  {
  // Perform any required action
  }
  您可以将所有需要执行的操作的代码都放在此处,也可以在某个对象公开的静态方法或实例方法中对所有这些代码进行分组:
  void Button1_Click(object sender, EventArgs e)
  {
  // Static method bound to the user action
  ActionManager.Button1Clicked();
  }
  您还可以组织对用户在控制器和操作中的活动进行响应的代码:
  void Button1_Click(object sender, EventArgs e)
  {
  // Static method bound to the user action
  ThisPageController controller = new
  ThisPageController();
  controller.Button1Clicked();
  }
  我现在假设,每个页面都有其自己的控制器组件,每个控制器都包含一系列在逻辑上绑定到可能的用户操作的公共方法。
  如何使控制器知道页面的状态,以及如何生成新视图?用户单击按钮且 ASP.NET 运行时接收到相应的 HTTP 请求并对其进行处理后,系统会在服务器上调用 Button1_Click。Button1_Click 方法由从 System.Web.UI.Page 派生的类公开。此类可以访问 UI 控件。
  下面对使控制器可以访问页面中的任一公共元素的控制器的签名做了简单的更改:
  void Button1_Click(object sender, EventArgs e)
  {
  // Static method bound to the user action
  ThisPageController controller = new ThisPageController(this);
  controller.Button1Clicked();
  }
  控制器签名现在可以接收对当前 ASP.NET 页面的引用,即视图。控制器方法执行其任务,然后仅更新服务器控件。ASP.NET 的标准页面生命周期将完成其余操作并为浏览器生成 HTML。
  页面生命周期
  图 1 显示了页面生命周期中的主要步骤。只要 ASP.NET 请求到达 Web 服务器并找到对其进行处理的 HTTP 处理程序后,就会执行图中介绍的步骤。您可以看到,简单的按钮单击操作的代码只是整个 ASP.NET 基础结构的一小部分。稍后您会看到,页面生命周期仅是 ASP.NET 运行时为了处理每个请求所执行的操作的一部分。
  图 1 ASP.NET 页面生命周期
  在执行 Button1_Click 中的代码前,ASP.NET 运行时会创建一个 Page 类的实例,以保存请求的 URL 的预期行为。此 Page 类按照图 1 中的步骤处理请求,并在适当的时候使用您的事件处理程序。
  在控制器类中的 Button1Clicked 方法中,您应该能够利用在纯代码隐藏事件处理程序中使用的同一语法访问页面中的任何控件。但引用 UI 控件(假设是 TextBox1)的成员不是 Page 类上的公共成员,您如何使控制器可以访问它们?您可以在代码隐藏类上实现一个自定义接口,使其包含您要在控制器中操作的 UI 控件的公共属性。下面是一个示例:
  public interface IThisPageView
  {
  TextBox TextBox1Property;
  ...
  }
  // In the codebehind class
  public TextBox TextBox1Property
  {
  get {return TextBox1;}
  }
  图 2 提供了一个示例控制器的示例。在 Button1Clicked 中,您可以进行常规的 ASP.NET Web 窗体编程,并且只需通过更新服务器控件的状态即可确定下一视图。现在,页面逻辑封装在一个单独的类中,此类可脱离页面进行开发。
  图 2 作为页面控制器的示例类
  public class ThisPageController
  {
  protected IThisPageView thePage;
  public ThisPageController(IThisPageView page)
  {
  thePage = page;
  }
  public void Button1Clicked() 
  {
  // Get the text currently stored in control TextBox1
  string text = thePage.TextBox1Property.Text;
  // Do something: i.e., turn to upper case
  string newText = text.ToUpper();
  // Update the text in control TextBox1
  _thePage.TextBox1Property.Text = newText;
  }
  }
  但是,此解决方案仍与页面中的控件紧密耦合。让我们看一下对 Model-View-Presenter (MVP) 模式的优化。我会简单回顾一下 MVC 和 MVP 模式的基础知识。
  原始 MVC 模式
  MVC 模式的发明要追溯到 1979 年,当时将该模式作为一种远离全能、自治前端的方法。自治视图是一个类,可显示目录、维护视图的状态信息,并整合了整个逻辑可以始终处理任何用户操作。
  此代码段为自治视图奠定了基础:
  void Button1_Click(object sender, EventArgs e)
  {
  // Perform any required action. All the code you
  // need to handle the event goes here. A page
  // built in this way is an autonomous view.
  // MVC fights autonomous views.
  }
  使用这样的整体化视图,您几乎无法构建独立于基础层的可测试表示层。应用关注点分离 (SoC) 的概念可帮助您获取任一组件的低级耦合和高度聚合的正确组合,因为它使您可以将各种功能保留在其各自的层中,而不是将它们混合到一起。其次,应用 SoC,可以更轻松地实现导航工作流以确定接下来显示的页面。
  通过联机方式可以阅读这篇介绍 MVC 方法的原创作品“如何使用模型-视图-控制器”。如果您尚未阅读此文章,我建议您无论是否熟悉 MVC 原理,都要看看这篇文章。现在,MVC 被认为是构建表示层的一种模式,但是它起初是用来构建完整应用程序的,从这个角度可以帮助您理解 MVC。
  MVC 模式的定义并不严谨,因此实施细节时很多地方都需要架构师自行斟酌。这也许是为什么会存在这么多 MVC 变体的原因。
  MVC 将应用程序拆分为三部分:模型、视图和控制器。模型引用应用程序数据,管理应用程序的功能并通知状态视图的更改。视图负责要向用户显示的内容。最后,控制器将用户操作映射到模型上的操作,然后更新当前视图或选择下一视图。这三个部分通常称为 MVC 的三层架构。图 3 以图形方式显示了自治视图和 MVC 应用程序的比较结果。
  图 3 从自治视图到 MVC
  在 MVC 中,用户与视图交互,然后视图捕获操作(如按钮单击)并将其转发给控制器。控制器确定要执行的操作并通过与模型交互来执行该操作。此模型实际上就是业务层加上一些额外的功能。特别是,模型会通知视图更新 UI 时可能需要的更改。
  虽然模型不了解视图的任何详细信息,但是模型和视图之间存在“观察者”关系。换句话说,视图保留对模型的引用并将其自身注册到模型以接收更改通知。接收到通知后,视图将从模型中获取新数据并对 UI 进行更新。图 4 演示了 MVC 交互的顺序图。请注意,在一些 MVC 实现中,由控制器通知视图所做的更改。
  图 4 标准 MVC 交互
  Model2:MVC 的 Web 变体
  设计 MVC 的初衷是面向桌面应用程序,但是由于其设计比较松散,因此被稍加修改应用到了 Web 上。常用的 MVC 模式的 Web 变体是 Model2。Model2 是现在的 ASP.NET MVC Framework 使用的模式的曾用名。
  Model2 模式通过浏览器向用户显示视图。用户操作由浏览器捕获,然后转换为新的 HTTP 请求。通过这种方式,请求从浏览器传到 Web 服务器中的特定组件(称为前端控制器)。前端控制器协调对 Web 应用程序提出的所有请求。
  在 ASP.NET 中,此前端控制器采用 URL 路由 HTTP 模块这种形式。HTTP 模块捕获请求,然后将其路由到相应的控制器以执行请求操作。返回操作方法后,控制器将对视图进行排序以刷新并为新 UI 传递新数据。视图输出被前端控制器 HTTP 模块捕获,然后发送回浏览器。
  图 5 显示典型 Model2 交互的顺序图。您应该注意到此图中仅包含常规步骤。例如,在该模式的 ASP.NET MVC 实现中,URL 路由组件除使用控制器外,还执行其他一些任务。
  图 5 ASP.NET MVC Framework 中的 Model2 交互
  原始 MVC 和 Model2 有些差别。您可以看到,视图和模型之间没有任何联系,而在前面提到的 MVC 原始设计中两者间存在“观察者”关系。用户操作也不是由视图来捕获和处理。控制器呈现视图并向其显式传递显示数据。
  在 ASP.NET 中,当提及 MVC 时,应该指定所说的是 Model2 还是手动实现的 MVC,本专栏开始曾简要介绍过此内容。(如果希望了解更多信息,请阅读《MSDN 杂志》上的文章 ASP.NET MVC Framework。
  ASP.NET MVC Framework 和手动 MVC
  为了使每个页面(或一组页面)都拥有一个控制器类,使用 ASP.NET MVC Framework 和手动实现 MVC 模式这两种方法有什么区别吗?在 SoC 方面,我没有发现任何显著差别。在以上两种情况下,您都有一个完全独立于视图和代表业务层或服务层网关的模型的控制器类。
  在可测试性方面,Model2 模式要优于 MVC 模式的原始设计,因为前者出色地强制分开了视图和模型,且视图非常精简,仅这一个方面就可以有效地测试为 ASP.NET MVC Framework 编写的代码。此外,在 Model2 中有一种可在视图和控制器之间建立的不严格的约定。在 ASP.NET MVC Framework 中,此约定由 ViewData 容器对象或视图类的 ViewPage<T> 基类表示,其中后者表示效果更好。
  与 ASP.NET Web 窗体相比,Model2 实现执行请求操作的速度更快。在 Model2 实现中,您只需要一个用来处理 HTTP 请求和调用控制器的组件,无需动态创建页面类;查找控制器依据的算法与 Web 窗体方案中查找 HTTP 处理程序相比简单得多;同样,您也不需要使用页面生命周期、视图状态或服务器控件。在 Model2 实现中,如 ASP.NET MVC Framework,您会拥有一个更灵活的运行时环境。接下来我们将进一步探讨此内容。
  在通过 Web 窗体编程模型实施的 MVC 方案中,所有用户操作均源于 HTTP 发布。Web 服务器捕获这些请求,然后将其映射到 Page 类。Page 类历经其整个生命周期,如图 1 所示。然后,Page 类找到正确的控制器并调用一种方法。执行此方法会修改模型。其余的页面生命周期涉及到刷新视图。
  在使用 ASP.NET MVC Framework 实施的 Model2 方案中,生成下一个视图的过程更简单。URL 路由模块解析 URL,并根据 URL 确定要使用的控制器,然后在所选的控制器上调用操作,该控制器为新视图收集新数据并传递此信息。该视图收集随后作为对捕获请求的响应返回到浏览器的 HTML。此视图是被动主题,实际上不需要进行测试。但是,您需要对控制器进行重点测试。
  Model2 是改善应用程序可测试性的优先选择。但是,如果您仍需要 Web 窗体运行时环境来支持会话、缓存、服务器控件甚至视图状态(随 MVC 模式一起提供),该怎么办?这是否意味着您不得不放弃 SoC 和可测试性?当然不是。您可以选择使用我在开始提到的 MVC 的另一变体 — MVP 模式。它适用于 Web 应用程序,且不需要临时运行时。
  MVP 模式
  MVC 的原始设计有一个重要缺陷:更新视图的机制。您在图 4 中看到的信息流显示视图将用户操作传送到控制器,而从模型接收更改通知。接下来,视图需要完全了解模型才能获取更新的数据以及进行刷新。MVP 是 MVC 的变体,能够更清晰地分离三层架构中的每个元素。图 6 显示了 MVP 模型中各部分之间的典型交互。
  图 6 运行中的 MVP 模式
  在 MVP 中,视图将用户输入转发给表示器,然后从表示器接收新数据以进行更新。反过来,表示器通过与模型交互来处理请求。
  在原始 MVC 中,没有任何明确约定规定视图需要哪些数据。在 MVC 中,视图保留对模型的引用并运行其自己的逻辑来选择所需数据,然后将其添加到 UI 元素。有了这个逻辑,视图不像其在进行测试时那样被动了。此外,视图在某种程度上依赖基础 UI 平台。
  在 MVP 中,表示器实际上是最终用户和应用程序之间的中介。表示器可以呈现视图并与模型交互,因此,大部分表示逻辑位于表示器中。由于表示器只是一个普通类,不包含任何 UI,因此从本质上来说,它是一个可测试性更高的类。您可以在 2006 年 8 月出版的《MSDN 杂志》中的“设计模式”专栏中看到如何在 ASP.NET 中实际实施 MVP 模式。MVP 由 Microsoft Web 客户端软件工厂提供本机支持,后者是一个与 ASP.NET 相关的应用程序块的集合。MSDN 中提供了 MVP 快速入门。
  MVP 是一个通用的 UI 模式,与 Model2 不同之处在于它并不专用于 Web,但 Model2 (ASP.NET MVC Framework) 和 MVP 基本上是相同的。有趣的是,这两个模式最初都不是设计为通用模式。
  从技术层面来讲,Model2 和 MVP 之间只有一点不同:控制器(在 MVP 中称为表示器)和视图之间的约定。Model2 中对约定的定义欠严谨,而 MVP 中的定义比较正规。
  在 MVP 中,视图实现一个界面,表示器仅使用该界面上的成员与视图进行交互。因此,例如表示器使用界面中的方法和 getter 读取视图中的输入数据,并使用界面中的方法和/或 setter 设置视图的新值。在 Model2 中,则使用强类型化的容器。
  通过使用 MVP,表示逻辑可以独立于 UI 平台存在,这样在不同的平台间重复使用相同的表示器会更容易。此外,相同的表示器可以处理同一应用程序中的不同的视图,因此可启用软件即服务 (SaaS) 方案。最后,利用 MVP,您还获得了一个组件(表示器),其中可能存储了一些逻辑可在页面间导航。是在内部实现逻辑还是使用 Windows Workflow Foundation (WF) 组件取决于您自己的意愿。
  页面控制器模式
  MVC、 Model2 和 MVP 是 ASP.NET 运行时设计的外部模式。ASP.NET 的 Web 窗体编程模型基于另一种设计模式,即页面控制器模式。页面控制器模式要求创建核心对象(动态创建的从代码隐藏类继承的类),该核心对象可以处理所有指向特定网页的 HTTP 请求(请参见图 7)。
  图 7 页面控制器结构
  如果自定义此模式,您可能会因为在页面间重复使用逻辑(包括导航逻辑)而受益。只需通过创建 Page 类的层次结构然后将导航逻辑和表示逻辑合并到层次结构较高的类中,以使该逻辑可供派生类使用,即可自定义该模式。自定义页面控制器是一个有效的方式,可以提高表示逻辑和导航逻辑的可重用性,而无需切换到一种全新的模式。
  应用 MVP 实际上意味着通过实现 Page 类上的接口并使用一个单独的组件(表示器)处理用户操作,以其他方式来设计页面的代码隐藏类。不会对运行时环境进行任何更改,因为请求将以常规方式路由至代码隐藏类。
  创建 ASP.NET MVC Framework 项目意味着对运行时环境进行更改以启用前端控制器(URL 路由模块),进而根据控制器上的操作处理请求。这两个选项都会对应用程序的开发产生影响。
  第一个要确定的问题是是否需要视图状态和服务器控件。在某些情况下,您可能不想使用这些控件。例如,如果没有服务器控件,使用 CSS 对 UI 进行样式设计会更简单。在当前情况下,使用包含服务器控件的 AJAX 比使用 ASP.NET MVC Framework 方案更简单。但如果没有丰富的服务器控件,很难构建包含大量数据输入或基于表的复杂视图的应用程序。因此,主要是找到一个平衡点。关键问题是您是否需要服务器控件和视图状态。这个问题没有明确的答案。
  如果您决定最好还是坚持使用 Web 窗体,但仍想偶尔使用 SoC,则可以选择使用 MVP。MVP 是一个非常重要的 UI 模式,在相对简单的应用程序中实施该模式可能有些浪费。另一方面,在企业级应用程序使用 MVP 效果非常好,因为此时您真正需要在多个平台之间和 SaaS 方案中重复使用尽可能多的表示逻辑。
100家互联网大公司java笔试题汇总,填问卷领取~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2023
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号