petshop:《解剖PetShop》系列之六 (1)

六 PetShop之表示层设计
表示层(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 典型的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页面中,进行如下的设置:
< %@ 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请求的接口,提供了如下的定义:
[AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.LinkDemand,
Level=AspNetHostingPermissionLevel.Minimal)]
public interface IHttpHandler
{
void ProcessRequest(HttpContext context);
bool IsReusable { get; }
}
Page类实现了ProcessRequest()方法,通过它可以设置Page对象的Request和Response属性,从而完成对用户请求 /相应的控制。然后Page类通过从Control类继承来的Load事件,将View与Model建立关联,如Products.aspx.cs所示:
public partial class Products : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//get page header and title
Page.Title = WebUtility.GetCategoryName(Request.QueryString[”categoryId”]);
}
}
事件机制恰好是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()方法,如下所示:
public partial class CheckOut : System.Web.UI.Page…
protected void wzdCheckOut_FinishButtonClick(object sender, WizardNavigationEventArgs e) {
if (Profile.ShoppingCart.CartItems.Count > 0) {
if (Profile.ShoppingCart.Count > 0) {
// display ordered items
CartListOrdered.Bind(Profile.ShoppingCart.CartItems);
// display total and credit card information
ltlTotalComplete.Text = ltlTotal.Text;
ltlCreditCardComplete.Text = ltlCreditCard.Text;
// create order
OrderInfo order = new OrderInfo(int.MinValue, DateTime.Now, User.Identity.Name, GetCreditCardInfo(), billingForm.Address, shippingForm.Address, Profile.ShoppingCart.Total, Profile.ShoppingCart.GetOrderLineItems(), null);
// insert
Order newOrder = new Order();
newOrder.Insert(order);
// destroy cart
Profile.ShoppingCart.Clear();
Profile.Save();
}
}
else {
lblMsg.Text = ”
Can not process the order. Your cart is empty.
Continue shopping
“;
wzdCheckOut.Visible = false;
}
}
在上面的一段代码中,非常典型地表达了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所示:
Tags:  petshop

延伸阅读

最新评论

发表评论