《解剖《解剖PetShop》之六:》之六:PetShop之表示层设计之表示层设计
表示层(Presentation Layer)通俗的讲就是界面层,也就是展示给客户的网页,本文主要讲解PetShop4.0的表示层设计,需要的朋友可以参考下。
六六 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 = "<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>";
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环境下,我们