亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

《解剖PetShop》系列之六

系統(tǒng) 1681 0
六 PetShop之表示層設(shè)計(jì)

表示層(Presentation Layer)的設(shè)計(jì)可以給系統(tǒng)客戶最直接的體驗(yàn)和最十足的信心。正如人與人的相交相識(shí)一樣,初次見(jiàn)面的感覺(jué)總是永難忘懷的。一件交付給客戶使用的產(chǎn)品,如果在用戶界面(User Interface,UI)上缺乏吸引人的特色,界面不友好,操作不夠體貼,即使這件產(chǎn)品性能非常優(yōu)異,架構(gòu)設(shè)計(jì)合理,業(yè)務(wù)邏輯都滿足了客戶的需求,卻仍然難以討得客戶的歡心。俗語(yǔ)云:“佛要金裝,人要衣裝”,特別是對(duì)于Web應(yīng)用程序而言,Web網(wǎng)頁(yè)就好比人的衣裝,代表著整個(gè)系統(tǒng)的身份與臉面,是招徠“顧客”的最大賣點(diǎn)。

“獻(xiàn)丑不如藏拙”,作為藝術(shù)細(xì)胞缺乏的我,并不打算在用戶界面的美術(shù)設(shè)計(jì)上大做文章,是以本書略過(guò)不提。本章所關(guān)注的表示層設(shè)計(jì),還是以架構(gòu)設(shè)計(jì)的角度,闡述在表示層設(shè)計(jì)中對(duì)模式的應(yīng)用,ASP.NET控件的設(shè)計(jì)與運(yùn)用,同時(shí)還包括了對(duì)ASP.NET 2.0新特色的介紹。

6.1 MVC模式

表示層設(shè)計(jì)中最重要的模式是MVC(Model-View-Controller,即模型-視圖-控制器)模式。MVC模式最早是由SmallTalk語(yǔ)言研究團(tuán)提出的,被廣泛應(yīng)用在用戶交互應(yīng)用程序中。Controller根據(jù)用戶請(qǐng)求(Response)修改Model的屬性,此時(shí)Event(事件)被觸發(fā),所有依賴于Model的View對(duì)象會(huì)自動(dòng)更新,并基于Model對(duì)象產(chǎn)生一個(gè)響應(yīng)(Response)信息,返回給Controller。Martin Fowler在《企業(yè)應(yīng)用架構(gòu)模式》一書中,展示了MVC模式應(yīng)用的全過(guò)程,如圖6-1所示:

6-1.gif

圖6-1 典型的MVC模式

如果將MVC模式拆解為三個(gè)獨(dú)立的部分:Model、View、Controller,我們可以通過(guò)GOF設(shè)計(jì)模式來(lái)實(shí)現(xiàn)和管理它們之間的關(guān)系。在體系架構(gòu)設(shè)計(jì)中,業(yè)務(wù)邏輯層的領(lǐng)域?qū)ο笠约皵?shù)據(jù)訪問(wèn)層的數(shù)據(jù)值對(duì)象都屬于MVC模式的Model對(duì)象。如果要管理Model與View之間的關(guān)系,可以利用Observer模式,View作為觀察者,一旦Model的屬性值發(fā)生變化,就會(huì)通知View基于Model的值進(jìn)行更新。而Controller作為控制用戶請(qǐng)求/響應(yīng)的對(duì)象,則可以利用Mediator模式,專門負(fù)責(zé)請(qǐng)求/響應(yīng)任務(wù)之間的調(diào)節(jié)。而對(duì)于View本身,在面向組件設(shè)計(jì)思想的基礎(chǔ)上,我們通常將它設(shè)計(jì)為組件或者控件,這些組件或者控件根據(jù)自身特性的不同,共同組成一種類似于遞歸組合的對(duì)象結(jié)構(gòu),因而我們可以利用Composite模式來(lái)設(shè)計(jì)View對(duì)象。

然而在.NET平臺(tái)下,我們并不需要自己去實(shí)現(xiàn)MVC模式。對(duì)于View對(duì)象而言,ASP.NET已經(jīng)提供了常用的Web控件,我們也可以通過(guò)繼承System.Web.UI.UserControl,自定義用戶控件,并利用ASPX頁(yè)面組合Web控件來(lái)實(shí)現(xiàn)視圖。ASP.NET定義了System.Web.UI.Page類,它相當(dāng)于MVC模式的Controller對(duì)象,可以處理用戶的請(qǐng)求。由于利用了codebehind技術(shù),使得用戶界面的顯示與UI實(shí)現(xiàn)邏輯完全分離,也即是說(shuō),View對(duì)象與Controller對(duì)象成為相對(duì)獨(dú)立的兩部分,從而有利于代碼的重用性。比較ASP而言,這種編程方式更符合開(kāi)發(fā)人員的編程習(xí)慣,同時(shí)有利于開(kāi)發(fā)人員與UI設(shè)計(jì)人員的分工與協(xié)作。至于Model對(duì)象,則為業(yè)務(wù)邏輯層的領(lǐng)域?qū)ο蟆4送猓?NET平臺(tái)通過(guò)ADO.NET提供了DataSet對(duì)象,便于與Web控件的數(shù)據(jù)源綁定。

6.2 Page Controller模式的應(yīng)用

通觀PetShop的表示層設(shè)計(jì),充分利用了ASP.NET的技術(shù)特點(diǎn),通過(guò)Web頁(yè)面與用戶控件控制和展現(xiàn)視圖,并利用codebehind技術(shù)將業(yè)務(wù)邏輯層的領(lǐng)域?qū)ο蠹尤氲奖硎緦訉?shí)現(xiàn)邏輯中,一個(gè)典型的Page Controller模式呼之欲出。

Page Controller模式是Martin Fowler在《企業(yè)應(yīng)用架構(gòu)模式》中最重要的表示層模式之一。在.NET平臺(tái)下,Page Controller模式的實(shí)現(xiàn)非常簡(jiǎn)單,以Products.aspx頁(yè)面為例。首先在aspx頁(yè)面中,進(jìn)行如下的設(shè)置:

<% @PageAutoEventWireup = " true " Language = " C# " MasterPageFile = " ~/MasterPage.master " Title = " Products " Inherits = " PetShop.Web.Products " CodeFile = " ~/Products.aspx.cs " %>

Aspx頁(yè)面繼承自System.Web.UI.Page類。Page類對(duì)象通過(guò)繼承System.Web.UI.Control類,從而擁有了Web控件的特性,同時(shí)它還實(shí)現(xiàn)了IHttpHandler接口。作為ASP.NET處理HTTP Web請(qǐng)求的接口,提供了如下的定義:

[AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level
= AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.LinkDemand,
Level
= AspNetHostingPermissionLevel.Minimal)]
public interface IHttpHandler
{
void ProcessRequest(HttpContextcontext);
bool IsReusable { get ;}
}

Page類實(shí)現(xiàn)了ProcessRequest()方法,通過(guò)它可以設(shè)置Page對(duì)象的Request和Response屬性,從而完成對(duì)用戶請(qǐng)求/相應(yīng)的控制。然后Page類通過(guò)從Control類繼承來(lái)的Load事件,將View與Model建立關(guān)聯(lián),如Products.aspx.cs所示:

public partial class Products:System.Web.UI.Page
{
protected void Page_Load( object sender,EventArgse)
{
// getpageheaderandtitle
Page.Title = WebUtility.GetCategoryName(Request.QueryString[ " categoryId " ]);
}

}

事件機(jī)制恰好是observer模式的實(shí)現(xiàn),當(dāng)ASPX頁(yè)面的Load事件被激發(fā)后,系統(tǒng)通過(guò)WebUtility類(在第28章中有對(duì)WebUtility類的詳細(xì)介紹)的GetCategoryName()方法,獲得Category值,并將其顯示在頁(yè)面的Title上。Page對(duì)象作為Controller,就好似一個(gè)調(diào)停者,用于協(xié)調(diào)View與Model之間的關(guān)系。

由于ASPX頁(yè)面中還可以包含Web控件,這些控件對(duì)象同樣是作為View對(duì)象,通過(guò)Page類型對(duì)象完成對(duì)它們的控制。例如在CheckOut.aspx頁(yè)面中,當(dāng)用戶發(fā)出CheckOut的請(qǐng)求后,作為System.Web.UI.WebControls.Winzard控件類型的wzdCheckOut,會(huì)在整個(gè)向?qū)н^(guò)程結(jié)束時(shí),觸發(fā)FinishButtonClick事件,并在該事件中調(diào)用領(lǐng)域?qū)ο驩rder的Insert()方法,如下所示:

public partial class CheckOut:System.Web.UI.Page

protected void wzdCheckOut_FinishButtonClick( object sender,WizardNavigationEventArgse) {
if (Profile.ShoppingCart.CartItems.Count > 0 ) {
if (Profile.ShoppingCart.Count > 0 ) {

// displayordereditems
CartListOrdered.Bind(Profile.ShoppingCart.CartItems);

// displaytotalandcreditcardinformation
ltlTotalComplete.Text = ltlTotal.Text;
ltlCreditCardComplete.Text
= ltlCreditCard.Text;

// createorder
OrderInfoorder = new OrderInfo( int .MinValue,DateTime.Now,User.Identity.Name,GetCreditCardInfo(),billingForm.Address,shippingForm.Address,Profile.ShoppingCart.Total,Profile.ShoppingCart.GetOrderLineItems(), null );

// insert
OrdernewOrder = new Order();
newOrder.Insert(order);

// destroycart
Profile.ShoppingCart.Clear();
Profile.Save();
}

}

else {
lblMsg.Text
= " <p><br>Cannotprocesstheorder.Yourcartisempty.</p><pclass=SignUpLabel><aclass=linkNewUserhref=Default.aspx>Continueshopping</a></p> " ;
wzdCheckOut.Visible
= false ;
}

}


在上面的一段代碼中,非常典型地表達(dá)了Model與View之間的關(guān)系。它通過(guò)獲取控件的屬性值,作為參數(shù)值傳遞給數(shù)據(jù)值對(duì)象OrderInfo,從而利用頁(yè)面上產(chǎn)生的訂單信息創(chuàng)建訂單對(duì)象,然后再調(diào)用領(lǐng)域?qū)ο驩rder的Inser()方法將OrderInfo對(duì)象插入到數(shù)據(jù)表中。此外,它還對(duì)領(lǐng)域?qū)ο骃hoppingCart的數(shù)據(jù)項(xiàng)作出判斷,如果其值等于0,就在頁(yè)面中顯示UI提示信息。此時(shí),View的內(nèi)容決定了Model的值,而Model值反過(guò)來(lái)又決定了View的顯示內(nèi)容。

6.3 ASP.NET控件

ASP.NET控件是View對(duì)象最重要的組成部分,它充分利用了面向?qū)ο蟮脑O(shè)計(jì)思想,通過(guò)封裝與繼承構(gòu)建一個(gè)個(gè)控件對(duì)象,使得用戶在開(kāi)發(fā)Web頁(yè)面時(shí),能夠重用這些控件,甚至自定義自己的控件。在第8章中,我已經(jīng)介紹了.NET Framework中控件的設(shè)計(jì)思想,通過(guò)引入一種“復(fù)合方式”的Composite模式實(shí)現(xiàn)了控件樹(shù)。在ASP.NET控件中,System.Web.UI.Control就是這棵控件樹(shù)的根,它定義了所有ASP.NET控件共有的屬性、方法和事件,并負(fù)責(zé)管理和控制控件的整個(gè)執(zhí)行生命周期。

Control基類并沒(méi)有包含UI的特定功能,如果需要提供與UI相關(guān)的方法屬性,就需要從System.Web.UI.WebControls.WebControl類派生。該類實(shí)際上也是Control類的子類,但它附加了諸如ForeColor、BackColor、Font等屬性。

除此之外,還有一個(gè)重要的類是System.Web.UI.UserControl,即用戶控件類,它同樣是Control類的子類。我們可以自定義一些用戶控件派生自UserControl,在Visual Studio的Design環(huán)境下,我們可以通過(guò)拖動(dòng)控件的方式將多種類型的控件組合成一個(gè)自定義用戶控件,也可以在codebehind方式下,為自定義用戶控件類添加新的屬性和方法。

整個(gè)ASP.NET控件類的層次結(jié)構(gòu)如圖6-2所示:

6-2.gif

圖6-2 ASP.NET控件類的層次結(jié)構(gòu)

ASP.NET控件的執(zhí)行生命周期如表6-1所示:

階段

控件需要執(zhí)行的操作
要重寫的方法或事件
初始化
初始化在傳入 Web 請(qǐng)求生命周期內(nèi)所需的設(shè)置。
Init 事件( OnInit 方法)
加載視圖狀態(tài)
在此階段結(jié)束時(shí),就會(huì)自動(dòng)填充控件的 ViewState 屬性,控件可以重寫 LoadViewState 方法的默認(rèn)實(shí)現(xiàn),以自定義狀態(tài)還原。
LoadViewState 方法
處理回發(fā)數(shù)據(jù)
處理傳入窗體數(shù)據(jù),并相應(yīng)地更新屬性。
注意:只有處理回發(fā)數(shù)據(jù)的控件參與此階段。
LoadPostData 方法(如果已實(shí)現(xiàn) IPostBackDataHandler
加載
執(zhí)行所有請(qǐng)求共有的操作,如設(shè)置數(shù)據(jù)庫(kù)查詢。此時(shí),樹(shù)中的服務(wù)器控件已創(chuàng)建并初始化、狀態(tài)已還原并且窗體控件反映了客戶端的數(shù)據(jù)。
Load 事件( OnLoad 方法)
發(fā)送回發(fā)更改通知
引發(fā)更改事件以響應(yīng)當(dāng)前和以前回發(fā)之間的狀態(tài)更改。
注意:只有引發(fā)回發(fā)更改事件的控件參與此階段。
RaisePostDataChangedEvent 方法(如果已實(shí)現(xiàn) IPostBackDataHandler
處理回發(fā)事件
處理引起回發(fā)的客戶端事件,并在服務(wù)器上引發(fā)相應(yīng)的事件。
注意:只有處理回發(fā)事件的控件參與此階段。
RaisePostBackEvent 方法(如果已實(shí)現(xiàn) IPostBackEventHandler
預(yù)呈現(xiàn)
在呈現(xiàn)輸出之前執(zhí)行任何更新。可以保存在預(yù)呈現(xiàn)階段對(duì)控件狀態(tài)所做的更改,而在呈現(xiàn)階段所對(duì)的更改則會(huì)丟失。
PreRender 事件 OnPreRender 方法
保存狀態(tài)
在此階段后,自動(dòng)將控件的 ViewState 屬性保持到字符串對(duì)象中。此字符串對(duì)象被發(fā)送到客戶端并作為隱藏變量發(fā)送回來(lái)。為了提高效率,控件可以重寫 SaveViewState 方法以修改 ViewState 屬性。
SaveViewState 方法
呈現(xiàn)
生成呈現(xiàn)給客戶端的輸出。
Render 方法
處置
執(zhí)行銷毀控件前的所有最終清理操作。在此階段必須釋放對(duì)昂貴資源的引用,如數(shù)據(jù)庫(kù)鏈接。
Dispose 方法
卸載
執(zhí)行銷毀控件前的所有最終清理操作。控件作者通常在 Dispose 中執(zhí)行清除,而不處理此事件。
UnLoad 事件( On UnLoad 方法)

表6-1 ASP.NET控件的執(zhí)行生命周期

在這里,控件設(shè)計(jì)利用了Template Method模式,Control基類提供了大部分protected虛方法,留待其子類改寫其方法。以PetShop 4.0為例,就定義了兩個(gè)ASP.NET控件,它們都屬于System.Web.UI.WebControls.WebControl的子類。其中,CustomList控件派生自System.Web.UI.WebControls.DataList,CustomGrid控件則派生自System.Web.UI.WebControls.Repeater。

由于這兩個(gè)控件都改變了其父類控件的呈現(xiàn)方式,故而,我們可以通過(guò)重寫父類的Render虛方法,完成控件的自定義。例如CustomGrid控件:

public class CustomGrid:Repeater…
// Staticconstants
protected const string HTML1 = " <tablecellpadding=0
cellspacing = 0 >< tr >< tdcolspan = 2 > " ;
protected const string HTML2 = " </td></tr><tr><tdclass=pagingalign=left> " ;
protected const string HTML3 = " </td><tdalign=rightclass=paging> " ;
protected const string HTML4 = " </td></tr></table> " ;
private static readonly RegexRX = new Regex( @" ^&page=\d+ " ,
RegexOptions.Compiled);
private const string LINK_PREV = " <ahref=?page={0}>&#060;&nbsp;Previous</a> " ;
private const string LINK_MORE = " <ahref=?page={0}>More&nbsp;&#062;</a> " ;
private const string KEY_PAGE = " page " ;
private const string COMMA = " ? " ;
private const string AMP = " & " ;

override protected void Render(HtmlTextWriterwriter) {

// Checkthereissomedataattached
if (ItemCount == 0 ) {
writer.Write(emptyText);
return ;
}

// Maskthequery
string query = Context.Request.Url.Query.Replace(COMMA,AMP);
query
= RX.Replace(query, string .Empty);
// Writeoutthefirstpartofthecontrol,thetableheader
writer.Write(HTML1);
// Calltheinheritedmethod
base .Render(writer);
// Writeoutatablerowclosure
writer.Write(HTML2);
// Determinwhethernextandpreviousbuttonsarerequired
// Previousbutton?
if (currentPageIndex > 0 )
writer.Write(
string .Format(LINK_PREV,(currentPageIndex - 1 ) + query));
// Closethetabledatatag
writer.Write(HTML3);

// Nextbutton?
if (currentPageIndex < PageCount)
writer.Write(
string .Format(LINK_MORE,(currentPageIndex + 1 ) + query));

// Closethetable
writer.Write(HTML4);
}

由于CustomGrid繼承自Repeater控件,因而它同時(shí)還繼承了Repeater的DataSource屬性,這是一個(gè)虛屬性,它默認(rèn)的set訪問(wèn)器屬性如下:

public virtual object DataSource
{
get {…}
set
{
if (((value != null ) && ! (value is IListSource)) && ! (value is IEnumerable))
{
throw new ArgumentException(SR.GetString( " Invalid_DataSource_Type " , new object [] { this .ID} ));
}

this .dataSource = value;
this .OnDataPropertyChanged();
}

}

對(duì)于CustomGrid而言,DataSource屬性有著不同的設(shè)置行為,因而在定義CustomGrid控件的時(shí)候,需要改寫DataSource虛屬性,如下所示:

private IListdataSource;
private int itemCount;

override public object DataSource {
set {
// ThistrycatchblockistoavoidissueswiththeVS.NETdesigner
// ThedesignerwilltryandbindadatasourcewhichdoesnotderivefromILIST
try {
dataSource
= (IList)value;
ItemCount
= dataSource.Count;
}

catch {
dataSource
= null ;
ItemCount
= 0 ;
}

}

}

當(dāng)設(shè)置的value對(duì)象值不為IList類型時(shí),set訪問(wèn)器就將捕獲異常,然后將dataSource字段設(shè)置為null。

由于我們改寫了DataSource屬性,因而改寫Repeater類的OnDataBinding()方法也就勢(shì)在必行。此外,CustomGrid還提供了分頁(yè)的功能,我們也需要實(shí)現(xiàn)分頁(yè)的相關(guān)操作。與DataSource屬性不同,Repeater類的OnDataBinding()方法實(shí)際上是繼承和改寫了Control基類的OnDataBinding()虛方法,而我們又在此基礎(chǔ)上改寫了Repeater類的OnDataBinding()方法:

override protected void OnDataBinding(EventArgse) {

// Workoutwhichitemswewanttorendertothepage
int start = CurrentPageIndex * pageSize;
int size = Math.Min(pageSize,ItemCount - start);
http:/
分享到:
評(píng)論

《解剖PetShop》系列之六


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 国产成人精品一区二区免费 | 亚洲欧美一级久久精品 | 久久最新| 亚洲国产成人久久精品影视 | 亚洲se主站 | 日本xoxo | 午夜时刻免费实验区观看 | 中文日产国产精品久久 | 美女牲交视频一级毛片 | 香蕉视频在线观看男女 | 国产精品亚洲欧美大片在线看 | 一级成人毛片免费观看欧美 | 国产成人免费全部网站 | 国产成人免费在线 | 91精品久久一区二区三区 | 国产成人一区二区三区免费观看 | 曰曰啪天天拍视频在线 | 成年女人看片免费视频频 | 国产成人精品久久免费动漫 | 亚州激情视频在线播放 | 又粗又大的机巴好爽视频视频 | 久久成人免费播放网站 | 青草久草视频 | 亚洲综合在线一区 | 欧美精品亚洲二区 | 欧美精品专区免费观看 | 一区二区三区国产精品 | 免费的爱爱视频 | 女bbbbxxx | 久久久久伊人 | 99在线观看视频免费 | 在线观看日本一区 | 久久www免费人成精品香蕉 | 亚洲综合成人网在线观看 | 91在线公开视频 | 久久一区二区免费播放 | 高清欧美一区二区免费影视 | 狠狠叉 | 天天爽夜夜操 | 老司机午夜免费影院 | 中文字幕在线看视频一区二区三区 |