第三章Web表示模式

体系结构设计者在设计第一个作品时比较精简和干练。在第一次设计时,并清除自己做什么,因此比较小心谨慎。第二个作品是最危险的一个作品,此时他会对第一个作品做修饰和润色,以及把第一次设计的边缘性设计思想都用在第二个作品,结果导致设计过头。

最初的Web很简单,只是有几个简单的Html页面组成,实现信息共享。随着业务的发展,需要根据业务来决定显示什么,于是开发了CGI编程,把大量的业务逻辑写到CGI中,然后输出到页面。随着发展,CGI编程模式受到了挑战,不能满足发展的需求,于是开发了Asp/JSP等叶面编程模型,这样可能把业务逻辑通过脚本嵌入到Html页面中,这样大量重复的代码充斥在系统中,导致难于维护。

在设计Web系统时有一个矛盾,那就是简单和复杂的冲突。我们在设计系统中力求简单和干练。但为了实现重用以及随着业务复杂性的增加,会不可避免的应用复杂设计方式。如何平衡这个二者呢?有一个办法可以帮组我们衡量,那就是这种引入的复杂性是为了满足当前需求呢,还是为了满足将来需求呢?如果是前者那就可以增加复杂性,这种是合理的。如果是为了将来预留的,那就是设计过头。因此在设计时,要了解关键Web程序设计问题、可能的解决方案以及利弊。

模式概述

此模式集群直接从长期存在的MVC开始,自从业务逻辑和表示逻辑分开后,该设计模式经久考验。该模式最初是在设计阶段编写,最后被微软映射到了Asp.NET MVC平台上。

在用 Microsoft® ASP.NET 实现 MVC 时,是从一个简单系统示例作为起点的:编写在一个页面上,而且将应用程序逻辑嵌入表示元素中。随着系统越来越复杂,使用了 ASP.NET 的代码隐藏功能来将表示代码(视图)与模型控制器代码分开。这始终能够很好地工作,直至要求迫使您不得不考虑在没有控制器的情况下重复使用模型代码以避免 应用程序冗余。此时,需要创建独立模型以抽象化业务逻辑,并使用代码隐藏功能使模型适应视图代码。然后,实现过程结束对该 MVC 方法测试含义的讨论。

迄今为止,使用 Model-View-Controller 模式时一直强调模型和视图;控制器扮演相对次要的角色。实际上,在此模式中工作的控制器是 ASP.NET 中的隐式控制器。它负责感知用户事件(请求和回发),并将这些事件写入适当的系统响应。

在 基于 Web 的动态应用程序中,在每次进行页请求时都会重复许多共同任务,如用户验证、确认、提取请求参数和查找与表示相关的数据库。如果不对这些任务进行管理,则会很快导致不必要的代码重复。因为这些任务与感知用户事件和确定正确的系统响应完全相关,所以用来放置该行为的逻辑位置是在控制器中。

功能更强大的控制

此群集中的下一个模式是 Page Controller (页面控制器),该模式是对 Model-View-Controller 的优化,并且能够满足下一个级别的复杂程度。此模式在页面范围中使用一个控制器,接受来自页请求的输入,针对模型调用请求的操作,然后确定要用作结果页面的正确视图。重复逻辑(如确认)被移到了基本控制器类中。

使用 ASP.NET 实现 Page Controller 用常见的外观示例阐述了 ASP.NET 内置的页面控制器的强大功能。实现过程还结合使用 Template Method(模板方法)模式与 Page Controller(页面控制器)模式来定义操作中的算法框架,并将其中的一些步骤交由子类负责

随 着应用程序越来越复杂,页面控制器最终在基类中累积了大量逻辑,此问题通常通过加深页面控制器的继承层次结构来解决。如果应用程序十分复杂,这两种因素都 会导致代码难以维护和扩展。同样,某些应用程序需要动态配置导航图,这有可能跨越多个页面控制器。达到这种复杂程度时,应该考虑 Front Controller(前端控制器)。

Front Controller , 是该目录中的下一个模式,它也是对 Model-View-Controller 的优化。在前端控制器中,所有请求都通过单个(通常是两部件)控制器来传送。控制器的第一个部件是处理程序,第二个部件是 Commands(命 令)的层次。命令本身是控制器的一部分,代表控制器触发的特定操作。在执行该操作之后,命令选择要使用哪个视图来呈现页面。通常,构建的此控制器框架使用配置 文件将请求映射到操作,因此,它在构建之后便于更改。当然,其缺点在于这种设计固有的复杂程度。

筛选器和缓存

此群集中的最后两种模式涉及到筛选器和缓存。

I ntercepting Filter (截取筛选器)为以下问题提供了解决方案:如何针对 HTTP 请求实现常见的预处理和后处理。在 Intercepting Filter 中,最适合执行非应用程序特定的常见任务,如安全性检查、日志记录、压缩、编码和解码。 Intercepting Filter 通常涉及到执行某个特定任务。在针对 HTTP 请求执行多项任务时,多个筛选器会链接到一起。在 ASP.NET 中使用 HTTP 模块实现 Intercepting Filter 强调可在 ASP.NET 中方便地实现此模式。

Page Cache (页面缓存)通过保留创建成本极高的常用动态网页的副本来满足 Web 应用程序日益增加的可伸缩性和性能要求。在最初创建该页面之后,会发送一份副本以便响应以后的请求。Page Cache 还讨论几个关键的缓存设计因素,如缓存刷新、数据刷新和缓存粒度。在 ASP.NET 中使用绝对过期实现 Page Cache 阐述了 ASP.NET 内置的页面缓存功能。

PageController(页面控制器)

上下文

您已经决定使用 Model-View-Controller (MVC) 模式来将动态 Web 应用程序的用户界面组件与业务逻辑分隔开来。要构建的应用程序将以动态方式构造网页,但网页间导航多为静态导航。

问题

如何以最佳方式为适度复杂的 Web 应用程序构建控制器,从而既能避免代码重复,又能实现重用性和灵活性?

影响因素

以下因素影响这种情况中的系统,在考虑上述问题解决方案时必须协调这些因素:

MVC 模式通常主要关注模型与视图之间的分隔,而对于控制器的关注较少。在许多胖客户端方案中,控制器和视图之间的分隔相对次要,因此通常会被忽略 [Fowler03]。但在瘦客户端应用程序中,视图和控制器本来就是分隔的,这是因为显示是在客户端浏览器中进行的,而控制器是服务器端应用程序的一部 分。因此有必要对控制器进行更为仔细的研究。

在动态 Web 应用程序中,多用户操作可以导致不同的控制器逻辑,然后显示相同页面。例如,在基于 Web 的简单电子邮件应用程序中,发送邮件和从收件箱中删除邮件这两个操作都可能将用户返回(刷新后的)收件箱页面。虽然这两种活动完成之后显示相同页面,但应 用程序必须根据上一页面以及用户所单击的按钮来执行不同的操作。

显示大多数动态网页的代码都包括非常相似的步骤:验证用户身份、从查询字符串或表单域中提取页面参数、收集会话信息、从数据源检索数据、生成页面动态部分以及添加适用的页眉和页脚。这可能导致大量的代码重复。

脚 本化服务器页面(例如 ASP.NET)可能很容易创建,但在应用程序不断增大时可能带来一些缺点。脚本化页面不能较好地分隔控制器和视图,因而降低了重用的可能性。例如,如果 多个操作将导致相同页面,在多个控制器之间重用显示代码则会比较困难,这是因为显示代码与控制器代码混合在一起。对散布于业务逻辑和显示逻辑之间的脚本化 服务器页面也更加难以进行测试和调试。最后,开发脚本化服务器页面要求同时精通开发业务逻辑和制作美观高效的 HTML 页面,而很少有人兼备这两项技能。基于上述考虑,因此有必要最大程度地减少脚本化服务器页面代码,而在实际类中开发业务逻辑。

正如 MVC 模式中的相关叙述,测试用户界面代码往往耗时而单调。如果可以分隔用户界面专用代码和实际业务逻辑,测试业务逻辑则会更为简单,且可重复性更强。对于显示部分和应用程序控制器部分都是如此。

通用外观和导航结构往往可以提高 Web 应用程序的可用性和品牌认知度。但通用外观可能会导致显示代码重复,特别是在脚本化服务器页面中嵌入代码时。因此,需要一种机制以提高页面间显示逻辑的重用性。

解决方案

使用 Page Controller 模式接受来自页面请求的输入、调用请求对模型执行的操作以及确定应用于结果页面的正确视图。分隔调度逻辑和所有视图相关代码。如果合适,创建用于所有页面控制器的公用基类,以避免代码重复并提高一致性和可测试性。图 1 显示了页面控制器与模型和视图的关系。

页面控制器可接收页面请求、提取所有相关数据、调用对模型的所有更新以及向视图转发请求。而视图又将根据该模型检索要显示的数据。定义独立页面控制 器将分隔模型与 Web 请求细节(例如会话管理,或使用查询字符串或隐藏表单域向页面传递参数)。按照这种基本形式,为 Web 应用程序中的每个链接创建控制器。控制器因而将变得非常简单,因为每次仅须考虑一个操作。

为每个网页(或操作)创建独立控制器可能会导致大量代码重复。因此应该创建 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 页面框架提供了可以实现这种分隔的完善机制,这种机制称为"代码隐藏类"。

变体

大多数情况下,页面控制器取决于基于 HTTP 的 Web 请求的具体细节。因此,页面控制器代码通常包含对 HTTP 头、查询字符串、表单域、多部分表单请求等的引用。因此在 Web 应用程序框架之外测试控制器代码非常困难。唯一方法是通过模拟 HTTP 请求和分析结果来测试控制器。这种类型的测试既费时且易出错。因此,要提高可测试性,可以将依赖 Web 的代码和不依赖 Web 的代码分别放入两个单独类中(请参阅图 3)。

在此示例中,AspNetController 封装了在应用程序框架 (ASP.NET) 上的所有依赖项。例如,它可以提取来自 Web 请求的所有传入参数,并使用独立于 Web 界面的方式(例如,使用集合)将其传递至 BaseController。此方法不仅可提高可测试性,而且允许通过其他用户界面重用该控制器代码,例如胖客户端界面或自定义脚本语言。

此方法的缺点在于增加了开销。现在新增了一个类,并且在处理每个请求前必须首先对其进行转换。因此,应尽可能控制器受环境影响的部分,并权衡选择降低依赖性与提高开发效率及执行效率。

结果上下文

使用 Page Controller 模式存在下列优缺点。

优点

简单性。由于每个动态网页由特定控制器处理,所以这些控制器仅需进行有限范围的处理,从而可以保持简单性。由于每个页面控制器仅处理一个网页,所以 Page Controller 尤其适用于导航结构简单的 Web 应用程序。

内置框架功能。在 其多数基本形式下,控制器已经置入大多数 Web 应用程序平台。例如,如果用户单击网页中指向由 ASP.NET 脚本生成的动态页面的链接,则 Web 服务器将分析与该链接关联的 URL,并执行相关 ASP.NET 页面。实际上,这些 ASP.NET 页面是用户所执行操作的控制器。ASP.NET 页面框架还提供了用于执行控制器代码的代码隐藏类。代码隐藏类提供了控制器和视图之间的更好分隔,并且允许创建合并所有控制器公用的功能控制器基类。有关 示例,请参阅"在 ASP.NET 中实现 Page Controller"。

增强型重用性。创建控制器基类可以减少代码重复,并允许在不同的页面控制器重用公用代码。可以通过在基类中实现重复逻辑来重用代码。然后,所有具体的 Page Controller 对象将自动继承此逻辑。如果该逻辑的实现对于不同页面而有所不同,则仍可以在基类中使用 Template Method,并实现基本的执行结构;具体子步骤的实现可能因页面的不同而有所变化。

可扩展性。通过使用帮助器类,可以很简便地扩展页面控制器。如果控制器中的逻辑过于复杂,则可以向帮助器类委派部分逻辑。除了继承之外,帮助器类还提供了另一种重用机制。

开发人员责任的分隔。使用 Page Controller 类有助于分离开发队伍中各成员的责任。控制器开发人员必须熟悉由应用程序所实现的域模型和业务逻辑。另一方面,视图设计者可以专注于结果的显示风格。

缺点

由于其简单性,Page Controller 是大多数动态 Web 应用程序的默认实现方式。但是应该了解下列限制:

每个页面一个控制器。 Page Controller 的 主要缺点是要为每个网页创建一个控制器。该特点非常适用于具有一组静态页面和简单导航路径的应用程序。有些较为复杂的应用程序要求对页面和页面间的导航映 射进行动态映射。如果将此逻辑分散于众多页面控制器,则可能导致应用程序难以维护,即使某些逻辑可以放入基本控制器。另外,Web 框架的内置功能可能会降低对 URL 和资源路径命名时的灵活程度(虽然可以使用 ISAPI 筛选器等较低级别的机制进行一定程度的补偿)。在这些方案中,请考虑使用 Front Controller 截取所有 Web 请求,并根据可配置规则将这些请求转发至相应处理程序。

较深的继承树。继承似乎是基于对象编程方式的既最可爱又最讨厌的功能之一。如果仅通过使用继承来重用公用功能,则可能降低继承层次结构的灵活性。有关详细信息,请参阅"在 ASP.NET 中实现 Page Controller"。

对于 Web 框架的依赖。在基本形式中,页面控制器仍然依赖于 Web 应用程序环境,且不能单独进行测试。可以使用包装机制来分隔依赖 Web 的部分,但这样需要增加一个级别的间接性。

测试考虑事项

因为Page Controller 依赖于 Web 应用程序框架的具体细节(例如,查询字符串和 HTTP 头),因此不能在 Web 框架之外对控制器类进行实例化和测试操作。如果需要对控制器类运行一组自动单元测试,则每次测试时都必须启动 Web 应用程序。然后必须使用可执行所需功能的格式提交 HTTP 请求。此配置为测试带来许多依赖性和不良影响。要提高可测试性,请考虑将业务逻辑(包括变得更加复杂的控制器逻辑)与依赖 Web 的代码分隔开来。

使用Micrisoft.net设计方案 第三章Web表示模式的更多相关文章

  1. 使用Micrisoft.net设计方案 第三章Web表示模式 Web模式集群详细介绍 Observer(观察器)

    在面向对象的编程中,对象同时包含数据和行为,这两者一起表示业务域的特定方面.使用对象生成应用程序的优点之一是可以将所有数据操作封装在对象内.这样, 就使对象成为独立的单位,并增加了在其他应用程序中重用 ...

  2. 第三章 web设计原则:

    程序员的修炼从优秀带卓越 第三章 web设计原则:    网站的评判标准     加载的速度要快     这到底是什么东西     给我看一个例子     清清楚楚的告诉我要做什么,并且扫除障碍   ...

  3. NancyFX 第三章 Web框架

    如果使用Nancy作为一个WEB框架而言,会有什么不同?实际上很多. 在使用Nancy框架为网页添加Rest节点和路由和之前的Rest框架中是相同的,这方面没有什么需要学习的了.Nancy采用一贯的处 ...

  4. 第三章 Web页面建设

    认识<q>元素: 简短的引用. 问:你去掉了双引号,换成了一个<q>元素,只是为了显示双引号?这样不是更复杂了吗? 答:不.在增加<q>元素之前,浏览器只知道这是一 ...

  5. 精通Web Analytics 2.0 (5) 第三章:点击流分析的奇妙世界:指标

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第三章:点击流分析的奇妙世界:指标 新的Web Analytics 2.0心态:搞定它.新的闪亮系列工具:是的.准备好了吗?当然 ...

  6. web—第三章XHTML

     web—第三章XHTML 又是一周 我们学的了做表单:一开始我以为表单是表格.但结果:表单是以采集和提交用户输入数据的,这样讲很迷,说简单点就是登陆端.比如:Facebook.twitter.Ins ...

  7. [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设

    [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设 敲黑板!! <q>元素添加短引用,<blockquote>添加长引用 在段落里添加引用就使用< ...

  8. (转)iOS Wow体验 - 第三章 - 用户体验的差异化策略

    本文是<iOS Wow Factor:Apps and UX Design Techniques for iPhone and iPad>第三章译文精选,其余章节将陆续放出.上一篇:Wow ...

  9. 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...

随机推荐

  1. 09--c++ 类的继承与派生

    c++ 类的继承与派生   一.基本概念 1.类的继承,是新的类从已有类那里得到已有的特性.或从已有类产生新类的过程就是类的派生.原有的类称为基类或父类,产生的新类称为派生类或子类.   2.派生类的 ...

  2. ASP.NET 页面验证cookie

    using System; using System.Collections.Generic; using System.Linq; using System.Web; /// <summary ...

  3. Restrictions.like("字段field","%表达式exp%");

    Restrictions.like("字段field","%表达式exp%");用hql语句就是 from table where field like '%e ...

  4. loadrunner报错总结

    1.报错   没有缓存空间可用   TCP超时释放时间?是解决刚才那个报错的? 解决方法如下  修改TcpTimedWaitDelay值为1和MaxUserPort值为65534.最后,重启!  完美 ...

  5. 爬虫系列(六) 用urllib和re爬取百度贴吧

    这篇文章我们将使用 urllib 和 re 模块爬取百度贴吧,并使用三种文件格式存储数据,下面先贴上最终的效果图 1.网页分析 (1)准备工作 首先我们使用 Chrome 浏览器打开 百度贴吧,在输入 ...

  6. PHP 设计模式之工厂模式 (静态工厂模式)

    ### 工厂模式: 由工厂类根据参数来决定创建出哪一种产品类的实例.工厂类是指包含了一个专门用来创建其他对象的方法的类.所谓按需分配,传入参数进行选择,返回具体的类.工厂模式的最主要作用就是对象创建的 ...

  7. jQuery升级踩坑之路

    1.使用了被废弃的jQuery.browser属性 jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support . 在更 ...

  8. python爬虫05 | 年轻人,不会正则表达式你睡得着觉?有点出息没有?

    现在 你已经会使用 python 模拟浏览器 进行一些 Http 的请求了 那么请求完之后 服务器返回给我们一堆源代码 我们可不是啥都要的啊 我们是有原则的 我们想要的东西 怎么能一股脑的啥都往自己兜 ...

  9. NLP问题特征表达基础 - 语言模型(Language Model)发展演化历程讨论

    1. NLP问题简介 0x1:NLP问题都包括哪些内涵 人们对真实世界的感知被成为感知世界,而人们用语言表达出自己的感知视为文本数据.那么反过来,NLP,或者更精确地表达为文本挖掘,则是从文本数据出发 ...

  10. (转载)关于初学者上传文件到github的方法

    说来也惭愧,我是最近开始用github,小白一个,昨天研究了一个下午.终于可以上传了,所以今天写点,一来分享是自己的一些经验,二来也是做个记录,万一哪天又不记得了:) 废话不多说,直接来,这次主要介绍 ...