解剖PetShop系列之六PetShop表示层设计

摘要:
PageController模式是Martin Fowler的企业应用程序架构模式中最重要的表示层模式之一。在下面。NET平台上,PageController模式的实现非常简单,以Products.aspx页面为例。作为控制器,Page对象充当协调视图和模型之间关系的中介。

表示层(Presentation Layer)的设计可以给系

统客户最直接的体验和最十足的信心。正如人与人的相交相识一样,初次见面的感觉总是永难忘怀的。一件交付给客户使用的产品,如 果在用户界面(User Interface,UI)上缺乏吸引人的特色,界面不友好,操作不够体贴,即使这件产品性能非常优异,架构设计合理,业务逻辑都满足了客户的需求,却仍 然难以讨得客户的欢心。俗语云:“佛要金装,人要衣装”,特别是对于Web应用程序而言,Web网页就好比人的衣装,代表着整个系统的身份与脸面,是招徕 “顾客”的最大卖点。

“献丑不如藏拙”,作为艺术细胞缺乏的我,并不打算在用户界面的美术设计上大做文章,是以本书略过不提。本章所关注的 表示层设计,还是以架构设计的角度,阐述在表示层设计中对模式的应用,ASP.NET控件的设计与运用,同时还包括了对ASP.NET 2.0新特色的介绍。

6.1 MVC模式

表示层设计中最重要的模式是MVC (Model-View-Controller,即模型-视图-控制器)模式。MVC模式最早是由SmallTalk语言研究团提出的,被广泛应用在用户 交互应用程序中。Controller根据用户请求(Response)修改Model的属性,此时Event(事件)被触发,所有依赖于Model的 View对象会自动更新,并基于Model对象产生一个响应(Response)信息,返回给Controller。Martin Fowler在《企业应用架构模式》一书中,展示了MVC模式应用的全过程,如图6-1所示:

6-1.gif

图6-1 典型的MVC模式

如 果将MVC模式拆解为三个独立的部分:Model、View、Controller,我们可以通过GOF设计模式来实现和管理它们之间的关系。在体系架构 设计中,业务逻辑层的领域对象以及数据访问层的数据值对象都属于MVC模式的Model对象。如果要管理Model与View之间的关系,可以利用 Observer模式,View作为观察者,一旦Model的属性值发生变化,就会通知View基于Model的值进行更新。而Controller作为 控制用户请求/响应的对象,则可以利用Mediator模式,专门负责请求/响应任务之间的调节。而对于View本身,在面向组件设计思想的基础上,我们 通常将它设计为组件或者控件,这些组件或者控件根据自身特性的不同,共同组成一种类似于递归组合的对象结构,因而我们可以利用Composite模式来设 计View对象。

然而在.NET平台下,我们并不需要自己去实现MVC模式。对于View对象而言,ASP.NET已经提供了常用的Web 控件,我们也可以通过继承System.Web.UI.UserControl,自定义用户控件,并利用ASPX页面组合Web控件来实现视图。 ASP.NET定义了System.Web.UI.Page类,它相当于MVC模式的Controller对象,可以处理用户的请求。由于利用了 codebehind技术,使得用户界面的显示与UI实现逻辑完全分离,也即是说,View对象与Controller对象成为相对独立的两部分,从而有 利于代码的重用性。比较ASP而言,这种编程方式更符合开发人员的编程习惯,同时有利于开发人员与UI设计人员的分工与协作。至于Model对象,则为业 务逻辑层的领域对象。此外,.NET平台通过ADO.NET提供了DataSet对象,便于与Web控件的数据源绑定。

6.2 Page Controller模式的应用

通观PetShop的表示层设计,充分利用了ASP.NET的技术特点,通过Web页面与用户控件控制和展现视图,并利用codebehind技术将业务逻辑层的领域对象加入到表示层实现逻辑中,一个典型的Page Controller模式呼之欲出。

Page Controller模式是Martin Fowler在《企业应用架构模式》中最重要的表示层模式之一。在.NET平台下,Page Controller模式的实现非常简单,以Products.aspx页面为例。首先在aspx页面中,进行如下的设置:

解剖PetShop系列之六PetShop表示层设计第2张<%@ Page AutoEventWireup="true" Language="C#" MasterPageFile="~/MasterPage.master" Title="Products" Inherits="PetShop.Web.Products" CodeFile="~/Products.aspx.cs"%>

Aspx 页面继承自System.Web.UI.Page类。Page类对象通过继承System.Web.UI.Control类,从而拥有了Web控件的特 性,同时它还实现了IHttpHandler接口。作为ASP.NET处理HTTP Web请求的接口,提供了如下的定义:

解剖PetShop系列之六PetShop表示层设计第2张[AspNetHostingPermission(SecurityAction.InheritanceDemand,
解剖PetShop系列之六PetShop表示层设计第2张Level
=AspNetHostingPermissionLevel.Minimal),
解剖PetShop系列之六PetShop表示层设计第2张AspNetHostingPermission(SecurityAction.LinkDemand,
解剖PetShop系列之六PetShop表示层设计第2张Level
=AspNetHostingPermissionLevel.Minimal)]
解剖PetShop系列之六PetShop表示层设计第2张
publicinterface IHttpHandler
解剖PetShop系列之六PetShop表示层设计第8张解剖PetShop系列之六PetShop表示层设计第9张
解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第11张      
void ProcessRequest(HttpContext context);
解剖PetShop系列之六PetShop表示层设计第12张解剖PetShop系列之六PetShop表示层设计第13张      
bool IsReusable 解剖PetShop系列之六PetShop表示层设计第10张{ get; }
解剖PetShop系列之六PetShop表示层设计第15张}

解剖PetShop系列之六PetShop表示层设计第2张

Page 类实现了ProcessRequest()方法,通过它可以设置Page对象的Request和Response属性,从而完成对用户请求/相应的控制。 然后Page类通过从Control类继承来的Load事件,将View与Model建立关联,如Products.aspx.cs所示:

解剖PetShop系列之六PetShop表示层设计第2张public partial class Products : System.Web.UI.Page
解剖PetShop系列之六PetShop表示层设计第8张解剖PetShop系列之六PetShop表示层设计第9张
解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第11张    
protectedvoid Page_Load(object sender, EventArgs e)
解剖PetShop系列之六PetShop表示层设计第12张解剖PetShop系列之六PetShop表示层设计第13张    
解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第11张        
//get page header and title
解剖PetShop系列之六PetShop表示层设计第11张
         Page.Title = WebUtility.GetCategoryName(Request.QueryString["categoryId"]);
解剖PetShop系列之六PetShop表示层设计第27张     }

解剖PetShop系列之六PetShop表示层设计第15张}

解剖PetShop系列之六PetShop表示层设计第2张

事 件机制恰好是observer模式的实现,当ASPX页面的Load事件被激发后,系统通过WebUtility类(在第28章中有对 WebUtility类的详细介绍)的GetCategoryName()方法,获得Category值,并将其显示在页面的Title上。Page对象 作为Controller,就好似一个调停者,用于协调View与Model之间的关系。

由于ASPX页面中还可以包含Web控件,这些控 件对象同样是作为View对象,通过Page类型对象完成对它们的控制。例如在CheckOut.aspx页面中,当用户发出CheckOut的请求后, 作为System.Web.UI.WebControls.Winzard控件类型的wzdCheckOut,会在整个向导过程结束时,触发 FinishButtonClick事件,并在该事件中调用领域对象Order的Insert()方法,如下所示:

解剖PetShop系列之六PetShop表示层设计第2张public partial class CheckOut : System.Web.UI.Page解剖PetShop系列之六PetShop表示层设计第10张
解剖PetShop系列之六PetShop表示层设计第2张
解剖PetShop系列之六PetShop表示层设计第8张解剖PetShop系列之六PetShop表示层设计第9张    
protectedvoid wzdCheckOut_FinishButtonClick(object sender, WizardNavigationEventArgs e) 解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第12张解剖PetShop系列之六PetShop表示层设计第13张        
if (Profile.ShoppingCart.CartItems.Count >0) 解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第12张解剖PetShop系列之六PetShop表示层设计第13张            
if (Profile.ShoppingCart.Count >0) 解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第11张
解剖PetShop系列之六PetShop表示层设计第11张                
// display ordered items
解剖PetShop系列之六PetShop表示层设计第11张
                 CartListOrdered.Bind(Profile.ShoppingCart.CartItems);
解剖PetShop系列之六PetShop表示层设计第11张
解剖PetShop系列之六PetShop表示层设计第11张                
// display total and credit card information
解剖PetShop系列之六PetShop表示层设计第11张
                 ltlTotalComplete.Text = ltlTotal.Text;
解剖PetShop系列之六PetShop表示层设计第11张                 ltlCreditCardComplete.Text
= ltlCreditCard.Text;
解剖PetShop系列之六PetShop表示层设计第11张
解剖PetShop系列之六PetShop表示层设计第11张                
// create order
解剖PetShop系列之六PetShop表示层设计第11张
                 OrderInfo order =new OrderInfo(int.MinValue, DateTime.Now, User.Identity.Name, GetCreditCardInfo(), billingForm.Address, shippingForm.Address, Profile.ShoppingCart.Total, Profile.ShoppingCart.GetOrderLineItems(), null);
解剖PetShop系列之六PetShop表示层设计第11张
解剖PetShop系列之六PetShop表示层设计第11张                
// insert
解剖PetShop系列之六PetShop表示层设计第11张
                 Order newOrder =new Order();
解剖PetShop系列之六PetShop表示层设计第11张                 newOrder.Insert(order);
解剖PetShop系列之六PetShop表示层设计第11张
解剖PetShop系列之六PetShop表示层设计第11张                
// destroy cart
解剖PetShop系列之六PetShop表示层设计第11张
                 Profile.ShoppingCart.Clear();
解剖PetShop系列之六PetShop表示层设计第11张                 Profile.Save();
解剖PetShop系列之六PetShop表示层设计第27张             }

解剖PetShop系列之六PetShop表示层设计第27张         }

解剖PetShop系列之六PetShop表示层设计第12张解剖PetShop系列之六PetShop表示层设计第13张        
else解剖PetShop系列之六PetShop表示层设计第10张{
解剖PetShop系列之六PetShop表示层设计第11张             lblMsg.Text
="<p><br>Can not process the order. Your cart is empty.</p><p class=SignUpLabel><a class=linkNewUser href=Default.aspx>Continue shopping</a></p>";
解剖PetShop系列之六PetShop表示层设计第11张             wzdCheckOut.Visible
=false;
解剖PetShop系列之六PetShop表示层设计第27张         }

解剖PetShop系列之六PetShop表示层设计第15张     }

解剖PetShop系列之六PetShop表示层设计第2张
解剖PetShop系列之六PetShop表示层设计第2张

在 上面的一段代码中,非常典型地表达了Model与View之间的关系。它通过获取控件的属性值,作为参数值传递给数据值对象OrderInfo,从而利用 页面上产生的订单信息创建订单对象,然后再调用领域对象Order的Inser()方法将OrderInfo对象插入到数据表中。此外,它还对领域对象 ShoppingCart的数据项作出判断,如果其值等于0,就在页面中显示UI提示信息。此时,View的内容决定了Model的值,而Model值反 过来又决定了View的显示内容。

6.3 ASP.NET控件

ASP.NET控件是View对象最重要的组成部分,它充分 利用了面向对象的设计思想,通过封装与继承构建一个个控件对象,使得用户在开发Web页面时,能够重用这些控件,甚至自定义自己的控件。在第8章中,我已 经介绍了.NET Framework中控件的设计思想,通过引入一种“复合方式”的Composite模式实现了控件树。在ASP.NET控件中, System.Web.UI.Control就是这棵控件树的根,它定义了所有ASP.NET控件共有的属性、方法和事件,并负责管理和控制控件的整个执 行生命周期。

Control基类并没有包含UI的特定功能,如果需要提供与UI相关的方法属性,就需要从 System.Web.UI.WebControls.WebControl类派生。该类实际上也是Control类的子类,但它附加了诸如 ForeColor、BackColor、Font等属性。

除此之外,还有一个重要的类是 System.Web.UI.UserControl,即用户控件类,它同样是Control类的子类。我们可以自定义一些用户控件派生自 UserControl,在Visual Studio的Design环境下,我们可以通过拖动控件的方式将多种类型的控件组合成一个自定义用户控件,也可以在codebehind方式下,为自定 义用户控件类添加新的属性和方法。

整个ASP.NET控件类的层次结构如图6-2所示:

6-2.gif

图6-2 ASP.NET控件类的层次结构

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇watir学习系列--Watir API介绍乌班图下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

动态创建DataGrid的模版列(转)

有的时候我们需要邦定很复杂的DataGrid,我们知道DataGrid,DataList等控件都有Template列,我们可以通过动态的邦定模版列来实现,复杂逻辑的邦定。由于Page继承TemplateControl,所以在Page对象里面就可以使用TemplateControl类里面的方法LoadTemplate,我们可以利用这个方法加载指定路径用户控件...

C#-web用户控件

从用户控件向页面中传递数据:法一:使用Session传递。1.在按钮点击时候,把值放到Session中去。2.重写页面的OnLoadComplete方法,在这个方法中把值从Session中取出来。注意:不要在Page_Load中取出Session 来。原因是:每次点击按钮的时候,Page_Load总是在按钮的Click之前触发。 法二:使用代理(委托 d...

WPF中UserControl的属性和事件

WPF中自定义控件有两种,一种是继承自control的自定义控件,另一种是继承自UserControl的用户控件。用户控件可以认为是一系列原生控件的集合。本文主要介绍如何创建一个用户控件,以及用户控件的自定义属性和事件。 一、创建一个用户控件 1、一种是直接创建用户控件工程,这样会生成DLL,使用时调用DLL 2、在当前工程中直接创建 创建之后会生成一...

WPF使用Winform PDFView控件

最近开发wpf项目中有一个模块需要显示PDF文件内容。由于WPF本身没有PDF加载控件(似乎有收费的我查到过类似的资料。如果有新的pdf控件也请通知我一下谢谢)。 项目使用之前也是从网上获取的资料,因此接下来的控件命名和项目命名完全沿用原文档。具体地址忘记了。这样也好感谢原作者。 不过里面的逻辑根据项目实际应用做了变工 。每人理解不同为了大家更快更好地的使...

(转载).net 缓存处理

概述 在ASP.NET应用程序构建过程中,为了提高应用程序的性能,缓存处理无疑是一个非常重要的环节。通常,我们将一些频繁被访问的数据,以及一些需要大量处理时间得出来的数据缓存在内存中,从而提高性能。例如,如果程序需要处理一张报表,这张报表的数据是关联的几张数据库表,并通过大量的计算得到的数据。我们知道表关联是比较耗时的,如果关联之后得出的数据再进行聚合排序...

WPF自定义控件的制作

  因为有时候需要定制化的控件,需要多个控件的组合及复杂功能的集成,这样可以考虑自定义用户控件。下面分享一个简单的数值增减功能的自定义控件作为说明。 效果图如下: 1、创建自定义用户控件(添加->新建项->用户控件) 2、编写XAML <UserControl x:Class="XXX.自定义控件.MyNumericUpDown"...