轉(zhuǎn)帖|其它|編輯:郝浩|2010-11-08 13:28:24.000|閱讀 625 次
概述:本篇主要講述ASP.NET應(yīng)用中如何進(jìn)行邏輯分層。本篇的前篇會(huì)從Smart UI 反模式和它的一些缺點(diǎn)開(kāi)始講述,然后一步步的講述如何邏輯分層,而且在后篇中也會(huì)給出一個(gè)ASP.NET設(shè)計(jì)中常用的僅供參考的分層架構(gòu)的Demo。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
前言:本篇不打算接著上一篇來(lái),這沒(méi)有關(guān)系,以為內(nèi)他們之間的聯(lián)系不大,以后我再補(bǔ)上。因?yàn)橹耙恢痹谡務(wù)撛O(shè)計(jì),也談了一些TDD的東西,大家反應(yīng)覺(jué)得講述的還是有點(diǎn)”空”,所以打算換一種方式:先講述一些例子,把一些思想穿插著講述,理論的東西最后最為總結(jié)。希望大家支持!
本篇主要講述ASP.NET應(yīng)用中如何進(jìn)行邏輯分層。本篇的前篇會(huì)從Smart UI 反模式和它的一些缺點(diǎn)開(kāi)始講述,然后一步步的講述如何邏輯分層,而且在后篇中也會(huì)給出一個(gè)ASP.NET設(shè)計(jì)中常用的僅供參考的分層架構(gòu)的Demo。
一個(gè)穩(wěn)定和易維護(hù)的系統(tǒng)必須建立在一個(gè)好的基礎(chǔ)之上。計(jì)劃和設(shè)計(jì)一個(gè)好的架構(gòu)對(duì)一個(gè)項(xiàng)目的成敗起著至關(guān)重要的作用。可能在我們一般做項(xiàng)目的時(shí)候,經(jīng)驗(yàn)告訴我們:3層,N層的設(shè)計(jì),基本就能把問(wèn)題解決了,很多的情況確實(shí)是這樣的。在提出一個(gè)設(shè)計(jì)的時(shí)候,常常要考慮為什么要這樣劃分結(jié)構(gòu),而且常常要承擔(dān)風(fēng)險(xiǎn)和責(zé)任,特別是萬(wàn)一這個(gè)項(xiàng)目因?yàn)樽畛醯脑O(shè)計(jì)而導(dǎo)致崩潰,那就郁悶了。所以設(shè)計(jì)的提出一定和考慮業(yè)務(wù)。
下面就先來(lái)看看Smart UI的設(shè)計(jì)方式。
Smart UI
想想我們最初是如何開(kāi)發(fā)ASP.NET的應(yīng)用的:在頁(yè)面設(shè)計(jì)界面中把界面布局好,然后雙擊控件就開(kāi)始編寫功能代碼。很多的時(shí)候把邏輯判斷和數(shù)據(jù)訪問(wèn)都寫在頁(yè)面的.cs的文件中。后來(lái)我們學(xué)習(xí)到了分層,逐漸的明白了這種方式的缺點(diǎn):導(dǎo)致業(yè)務(wù)邏輯代碼到處分散而且重復(fù),不利于以后的更改和維護(hù)等。
盡管有上述說(shuō)的一些缺點(diǎn),Smart UI還是有它的用途的,如為項(xiàng)目快速的建立一個(gè)原型或者開(kāi)發(fā)一個(gè)功能比較的小的項(xiàng)目。還有一個(gè)問(wèn)題,如何最初用Smart UI的方式開(kāi)發(fā)的小項(xiàng)目很成功,慢慢的變大,變復(fù)雜了,那么很多的問(wèn)題就出來(lái)了。就像Flower在架構(gòu)模式一書中提到的:盡量用領(lǐng)域模型來(lái)組織一個(gè)項(xiàng)目的業(yè)務(wù)邏輯,盡管在開(kāi)始的時(shí)候邏輯不復(fù)雜或者看不出這種方式的好處,一旦項(xiàng)目變化,好處就顯而易見(jiàn)了。在對(duì)項(xiàng)目原型開(kāi)發(fā)中,盡量不用Smart UI。
其實(shí)Smart UI最大的問(wèn)題就是:職責(zé)不清—把所有的東西全部寫在一起。
為了和以后講述的內(nèi)容的比較,我還是寫一個(gè)例子出來(lái),很多朋友都已經(jīng)對(duì)這種Smart UI的開(kāi)發(fā)方式很熟悉了,可以跳過(guò)下面的例子。
在例子中,我們會(huì)用電子商務(wù)中一個(gè)常見(jiàn)的場(chǎng)景:一個(gè)頁(yè)面來(lái)顯示一個(gè)產(chǎn)品的列表信息,如名字,推薦的零售價(jià)格(Recommend Retail Price),折扣,和庫(kù)存等。(如果朋友們?cè)敢猓梢哉罩旅娴牟襟E一起做)
1. 打開(kāi)Visual Studio,并且建立一個(gè)”空白的解決方案”,命名為:ASPPatterns.Chap3.SmartUI,然后添加一個(gè)新的Web項(xiàng)目,命名為:ASPPatterns.Chap3.SmartUI.Web.
2. 在新建的Web項(xiàng)目中右擊:Add—New Item,添加一個(gè)Sql Server的數(shù)據(jù)文件:Shop.mdf.

如下:
3. 在新加的數(shù)據(jù)庫(kù)文件上右擊,并且打開(kāi)。然后添加一個(gè)新表:如下:

其中ProductId設(shè)置為自動(dòng)標(biāo)示。
然后保存為Products表。
4. 添加一些測(cè)試的數(shù)據(jù):

5. 然后選擇Products表,并且把表拖放到Default.aspx頁(yè)面上。這樣之后,在頁(yè)面上就自動(dòng)添加一個(gè)GridView和SqlDataSource.
界面就如下圖:

6. 我我們添加額外的兩列來(lái)顯示折扣信息和庫(kù)存信息。Default.aspx的Source代碼最后如下:

7. 然后,我們?cè)贒efault.aspx.cs后編碼:
在上面的 GridView1_RowDataBound方法在GridView的每個(gè)row被創(chuàng)建的時(shí)候調(diào)用。這個(gè)方法獲取每個(gè)產(chǎn)品的推薦的零售價(jià)格RRP(Recommend Retail Price),然后調(diào)用 DisplayDiscount和DisplaySavings方法來(lái)獲取折扣和庫(kù)存,然后再更新UI的顯示。
在上面的代碼中,就將計(jì)算折扣和計(jì)算庫(kù)存的邏輯寫在了UI中,而且數(shù)據(jù)的訪問(wèn)代碼也寫在UI中了。這就意味著:如果我們想要在不同的頁(yè)面顯示產(chǎn)品的信息,那么這些邏輯就得一遍遍的重寫。如果我們?cè)诩右恍┬碌墓δ埽敲错?yè)面后面的代碼就開(kāi)始修改,開(kāi)始縫縫補(bǔ)補(bǔ)。
解決Smart UI的方法就是劃分職責(zé),我想大家都知道“單一職責(zé)的原則”,這個(gè)原則不僅僅適用于類,方法,而且對(duì)項(xiàng)目的層次劃分也有作用。分層,最主要的目的就是:把不通的功能放在各自對(duì)應(yīng)的地方,這樣清晰的職責(zé)劃分,也是對(duì)變化點(diǎn)進(jìn)行分離。
下面的圖就是一個(gè)典型的企業(yè)級(jí)ASP.NET項(xiàng)目的分層結(jié)構(gòu):

下面我們就來(lái)看看,按照我們的一般的分層的經(jīng)驗(yàn)來(lái)如何設(shè)計(jì)這功能:
1. 創(chuàng)建一個(gè)新的空白的解決方案,命名為ASPPatterns.Chap3.Layered.
2. 添加四個(gè)新的C#類庫(kù),分別命名為:
a) ASPPatterns.Chap3.Layered.Repository.
b) ASPPatterns.Chap3.Layered.Model.
c) ASPPatterns.Chap3.Layered.Service.
d) ASPPatterns.Chap3.Layered.Presentation
3. 添加一個(gè)新的Web程序,命名為ASPPatterns.Chap3.Layered.Web.。

注:朋友們一眼就應(yīng)該可以看出,這些類庫(kù)的命名是反映了一些DDD的一些概念,但是,不是說(shuō)在一個(gè)項(xiàng)目的開(kāi)發(fā)中用了這些概念名詞就表明就開(kāi)發(fā)的方式是DDD了。
3. 不同的類庫(kù)就分別承擔(dān)不同的職責(zé),而且每一層一般都只是引用自己的下一層,而且下一層不知道自己被上一層使用。本例中的引用關(guān)系如下:

這里我先提一下上面類庫(kù)的一起名字:盡管有關(guān)DDD和一些架構(gòu)模式的概念我在以后的文章中會(huì)講,但我這里還是先給大家提一下,目的僅僅是讓大家對(duì)這個(gè)例子有一些更好的了解。
在DDD中,一直主張業(yè)務(wù)模型,也就是我們常常所說(shuō)的業(yè)務(wù)類,例如之前例子中的Product,只關(guān)注自身的業(yè)務(wù)邏輯,而不管如何去獲取和保存數(shù)據(jù),這些對(duì)數(shù)據(jù)的操作完全交給另外的對(duì)象去執(zhí)行,也就是Repository,這樣就達(dá)到了DDD中所說(shuō)的PI(Persistence Ignore)。所以在上面的例子中,ASPPatterns.Chap3.Layered.Model就代表了一個(gè)業(yè)務(wù)模型,它之所以被Repository引用,是因?yàn)镽epository負(fù)責(zé)將Model的數(shù)據(jù)持久化到存儲(chǔ)設(shè)備中,而Model不管這些事情了。
在講ASPPatterns.Chap3.Layered.Service之前,首先給大家統(tǒng)一 一下Service的概念。
有時(shí)在類的設(shè)計(jì)過(guò)程中,有些行為不適合放在任何的一個(gè)類中,如果把這些行為放在一個(gè)不真正擁有它的類中,只能把類的職責(zé)搞混了。為了給這些行為一個(gè)安置的地方,我們常常把這些行為放在一個(gè)稱為服務(wù)的類中。
作為服務(wù)的類一般沒(méi)有狀態(tài)的,可以簡(jiǎn)單的作為一個(gè)提供操作接口實(shí)現(xiàn)。
在DDD中,Service也是用來(lái)提供一種服務(wù)的。很多人看到了DDD的類層次結(jié)構(gòu)是這樣的:Repository---Model---Service--- Presentation(包括本例),所以都以為Service只能出現(xiàn)在Model的上一層,如果看到Repository-- Service ---Model---Service--- Presentation這樣的層次結(jié)構(gòu),又作何感想。如果被這些所謂的結(jié)構(gòu)搞迷惑了,那就說(shuō)明對(duì)DDD的理解只是在于“形”上。Service就是向外部提供的功能接口,和我們常見(jiàn)的Web Service的概念很相似,例如的Web Service就是向外部系統(tǒng)提供一些功能的。
我們來(lái)看下面的一個(gè)圖:

有時(shí)候之所以要在Model層之上加上一個(gè)Service層,主要的原因就是實(shí)現(xiàn)粗顆粒度的API,往往和系統(tǒng)的User Case有一定的聯(lián)系。例如,如果在系統(tǒng)用例中要實(shí)現(xiàn)一個(gè)用戶訂單的處理,那么可能就涉及到Customer, Product,Order等類,當(dāng)然,如果我們調(diào)用這些類來(lái)共同完成這個(gè)任務(wù)是沒(méi)問(wèn)題的,但是這樣就向調(diào)用者暴露這些類之間的復(fù)雜的關(guān)系,而且如果處理的過(guò)程變化了,那么調(diào)用者的代碼就要改變,如果把這個(gè)處理的方法放在上面的任意一個(gè)類中,又顯得不倫不類,這里的Service功能就類似于設(shè)計(jì)模式中的Façade外觀模式。這樣就向外界提供簡(jiǎn)單的API,向外界提供訂單處理的服務(wù)!
所以在一般在DDD中業(yè)務(wù)層被劃分為兩個(gè)邏輯層:Model (提供細(xì)粒度的業(yè)務(wù)邏輯處理,也便于重用), Service(提供業(yè)務(wù)處理的流程,提供粗顆粒度的供外部調(diào)用的方法)。
但是,我們常見(jiàn)到的Model層之上的Service層僅僅只是對(duì)CRUD的再次封裝,一個(gè)可能的原因就是業(yè)務(wù)不是很復(fù)雜,這時(shí)其實(shí)這個(gè)Service層可以拿掉的,但是考慮到以后可能邏輯會(huì)更多更復(fù)雜,所以還是保留Service這層。
其實(shí)在Repository上的那個(gè)Service也是同樣的概念。例如發(fā)送郵件通知用戶的功能。例如上圖中的最上層的Service可以調(diào)用業(yè)務(wù)層和基礎(chǔ)設(shè)施層的Service來(lái)共同完成一個(gè)事情。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:博客轉(zhuǎn)載