Johnny's Collections

          生活總是有太多的無奈與失望,讓我們以在努力學(xué)習(xí)和工作中獲得的成就感和快樂來沖淡它們。

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            10 Posts :: 0 Stories :: 80 Comments :: 0 Trackbacks

          2010年5月27日 #

              一直對(duì)BIO、NIO、AIO不太理解,特別是阻塞與異步的區(qū)別。Google了一下,一篇文章中的4張圖很形象的表述了4種IO模型的原理和區(qū)別,收藏一下。

              首先,貼一張表示四種IO模型的圖:



          同步阻塞IO:


          同步非阻塞IO:



          異步阻塞IO:


          異步非阻塞IO:
          posted @ 2012-05-20 00:12 Johnny.Liang 閱讀(937) | 評(píng)論 (0)編輯 收藏

          6月下旬,由于個(gè)人發(fā)展的原因,離開了做銀行外包的F公司,也離開了S城,結(jié)束了2個(gè)半月短暫的S城之旅,重新回到了土生土長(zhǎng)的G城,也加入了一家互聯(lián)網(wǎng)公司,工作這么多年,都是做企業(yè)級(jí)應(yīng)用,這次是我第一次從事互聯(lián)網(wǎng)技術(shù)工作,我終于落網(wǎng)了。

          從事互聯(lián)網(wǎng)技術(shù)工作與企業(yè)級(jí)應(yīng)用有很大的不同,考慮的東西更多了。由于缺乏互聯(lián)網(wǎng)相關(guān)的開發(fā)經(jīng)驗(yàn),很多東西需要去學(xué)習(xí),同時(shí),加入新公司后,終于真正的當(dāng)上了Leader,這次終于可以自己一手一腳的組件自己的團(tuán)隊(duì)了,所以,除了技術(shù)工作,還有一大堆管理工作要做。幾個(gè)月來,晚上9點(diǎn)半過后下班,周末帶電腦回家繼續(xù)工作已經(jīng)成為了習(xí)慣。總之,每天都有忙不完的工作,生活的70%時(shí)間都在工作上。

          正因如此,本來想堅(jiān)持寫的博客暫停了,《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》的系列文章也暫停了很久。在此,向一直關(guān)注我的文章的同學(xué)說聲抱歉,我可能有一段時(shí)間要暫停寫博了。不過,技術(shù)依然是我的最愛,IT依然是我的事業(yè),等我在新公司的工作上軌之后,我就會(huì)回來。各位,再會(huì)!

          posted @ 2010-12-14 22:46 Johnny.Liang 閱讀(1155) | 評(píng)論 (3)編輯 收藏

          領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)系列文章(3)——有選擇性的使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)

           

          本系列的第一篇博文拋磚引玉,大談?lì)I(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的優(yōu)勢(shì),這里筆者還是希望以客觀的態(tài)度,談?wù)勵(lì)I(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的缺點(diǎn)及其不適合使用的場(chǎng)景,以讓讀者可以有選擇性的使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。

           

                 我們知道,沒有最好,只有最合適,設(shè)計(jì)也是一樣。因此,所謂設(shè)計(jì),就是以你和你的團(tuán)隊(duì)的知識(shí)、經(jīng)驗(yàn)和智慧,全面充分的考慮各種內(nèi)外因素后,在你們的設(shè)計(jì)方案中作出合理的選擇的過程。而這些影響你們選擇的因素主要有:

           

          • 技術(shù)框架的特征和約束(如果你的項(xiàng)目決定使用C語(yǔ)言進(jìn)行開發(fā),那么首先在設(shè)計(jì)方法上,就需要使用面向過程而非面向?qū)ο蟮脑O(shè)計(jì)方法)。

           

          • 時(shí)間的壓力和約束(你永遠(yuǎn)不可能告訴你的老板,給我10年時(shí)間,我和我的團(tuán)隊(duì)將為你設(shè)計(jì)出世界上最優(yōu)秀的軟件)。

           

          • 你的團(tuán)隊(duì)的能力、經(jīng)驗(yàn)、性格、價(jià)值觀等因素的約束(你不能期望一個(gè)長(zhǎng)期從事遺留系統(tǒng)維護(hù)項(xiàng)目或大部分成員是缺乏經(jīng)驗(yàn)的高校畢業(yè)生的團(tuán)隊(duì)能很好的按照你的設(shè)計(jì)意圖去實(shí)現(xiàn)你的高度抽象的優(yōu)秀設(shè)計(jì),同時(shí)你也別指望一幫家里經(jīng)濟(jì)條件不錯(cuò),本著過來熬時(shí)間的家伙會(huì)樂意與你一起刻苦鉆研、精益求精)。

           

          • 你的系統(tǒng)的特征(如果你想把一個(gè)足夠簡(jiǎn)單而且在可以預(yù)計(jì)的將來都不存在很大規(guī)模的需求變更的系統(tǒng)設(shè)計(jì)得很復(fù)雜,很精妙,具有很好的擴(kuò)展性,但要為此付出巨大的時(shí)間、人力成本,這顯然是一種不理智的過度設(shè)計(jì)(Over design))。

           

          • 其他外在因素的約束(你的項(xiàng)目需要參與投標(biāo),你必須壓縮人力、時(shí)間等以讓你的項(xiàng)目成本成為巨大的競(jìng)爭(zhēng)資本)。

           

          當(dāng)然,上述的考慮因素站在比較高的角度,通常是項(xiàng)目經(jīng)理、架構(gòu)師需要考慮的問題,但這當(dāng)中你應(yīng)該會(huì)得到一些啟發(fā)。回到我們的主題,我們首先看看,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)相對(duì)于傳統(tǒng)的面向過程式的設(shè)計(jì),有什么缺點(diǎn):

           

          • 復(fù)雜化:面向過程思維之所以一直很受歡迎,是因?yàn)樗苤庇^,非常符合大部分人的思維習(xí)慣,大部分人拿到一個(gè)問題,通常都是會(huì)很直觀的想第一步做什么、第二步做什么,如果怎樣,應(yīng)該怎樣,否則怎樣……,可以說,任何水平的程序員,都能很好的使用面向過程的方法進(jìn)行設(shè)計(jì)和開發(fā)。同時(shí),由于我們教育水平的落后和整體IT環(huán)境的制約,可以這樣說,真正掌握面向?qū)ο笏季S和設(shè)計(jì)方法的程序員的比例非常低(雖然絕大部分都使用面向?qū)ο蟮恼Z(yǔ)言和工具),而本身面向?qū)ο笏季S要求人有很好的抽象思維能力,因?yàn)槟阈枰岩粋€(gè)復(fù)雜的系統(tǒng)一層層的抽象為簡(jiǎn)單的部分,需要把現(xiàn)實(shí)世界的事物(有些是可見的,但有些是不可見)合理的抽象為計(jì)算機(jī)世界的不同元素。這些都不是一些很容易做的事情,要做得好,就更難。


          • 團(tuán)隊(duì)的抗拒:如果你的團(tuán)隊(duì)(很大可能)大部分人都習(xí)慣于用很直觀的面向過程的方式進(jìn)行設(shè)計(jì)和開發(fā),你需要推動(dòng)你的團(tuán)隊(duì)轉(zhuǎn)換思維來采用面向?qū)ο蟮姆绞竭M(jìn)行領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),通常會(huì)遭到多數(shù)人的抗拒。因?yàn)槿硕际怯卸栊缘模麄兞?xí)慣安于現(xiàn)狀,而改變是痛苦的,他們要付出額外的努力,需要進(jìn)行學(xué)習(xí),但以筆者的經(jīng)驗(yàn),相當(dāng)一部分程序員,特別是有一定工作年限的程序員,他們從事IT工作都只是為了獲得一份不錯(cuò)的報(bào)酬,因此他們的學(xué)習(xí)動(dòng)力非常有限,而且,他們都相當(dāng)自負(fù),被說服的難度比較大。



          • 管理、開發(fā)和維護(hù)的成本高:復(fù)雜度更高,意味著你需要花更多的時(shí)間進(jìn)行設(shè)計(jì),同時(shí)需要花出額外的時(shí)間進(jìn)行必要的培訓(xùn),而且需要有更完善的文檔(設(shè)計(jì)文檔,API文檔,培訓(xùn)文檔等)。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的抽象程度比較高,因此必需有良好的文檔,否則,隨著項(xiàng)目的不斷迭代、升級(jí)和維護(hù),它很容易因?yàn)楹髞碚叩恼`解而慢慢回歸面向過程的設(shè)計(jì),甚至?xí)兊?#8220;四不象”,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的成本優(yōu)勢(shì)是隨著時(shí)間的推移慢慢體現(xiàn)的(見下圖),如果出現(xiàn)這種情況,所有前面付出的努力都會(huì)付諸東流。


          系統(tǒng)的初始階段,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)需要付出更大的成本,但隨著時(shí)間的推移,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的成本效益優(yōu)勢(shì)會(huì)逐步顯現(xiàn)

          • 性能比較低:使用純面向?qū)ο蟮姆绞竭M(jìn)行領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的程序,其系統(tǒng)開銷通常要比面向過程設(shè)計(jì)的程序高,從而性能相對(duì)較低(關(guān)于具體的例子,后續(xù)的博文會(huì)提及)。

           

          那么,假設(shè)我們?cè)跁r(shí)間、團(tuán)隊(duì)能力及各種資源都允許的情況下,是否就可以麻木的全盤使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)呢?正如本博文的標(biāo)題一樣,答案是否定的,我們需要有選擇性的使用。讓我們來看看一些指導(dǎo)性原則:

           

          • 使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),并不代表整個(gè)系統(tǒng)的方方面面都必須遵從領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的原則,需要根據(jù)實(shí)際情況,讓適合的部分使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),讓不適合的部分使用面向過程的設(shè)計(jì)。


          • 對(duì)于那些業(yè)務(wù)規(guī)則非常簡(jiǎn)單,通常只有增、刪、改、查的簡(jiǎn)單操作,而且也不大可能發(fā)生大規(guī)模需求變更的模塊,可以讓業(yè)務(wù)實(shí)體成為一個(gè)“貧血模型”,例如一些基礎(chǔ)數(shù)據(jù):系統(tǒng)參數(shù)、商品類型、國(guó)家、地址信息(注:對(duì)于這點(diǎn),本人持保留態(tài)度,因?yàn)檫@些業(yè)務(wù)雖然非常簡(jiǎn)單,但既然選擇了領(lǐng)取驅(qū)動(dòng)設(shè)計(jì),即使把這些業(yè)務(wù)實(shí)體設(shè)計(jì)為“充血模型”,即把極其簡(jiǎn)單的業(yè)務(wù)邏輯也封裝在業(yè)務(wù)實(shí)體中,也并不比使用“貧血模型”成本高,而它卻帶來了統(tǒng)一設(shè)計(jì)風(fēng)格的好處)。


          • 對(duì)于查詢操作,特別是復(fù)雜的查詢操作,出于性能的考慮,可以用結(jié)構(gòu)化查詢邏輯一次性完成,并把這些邏輯封裝在Repository(即技術(shù)上的DAO)中(這方面的具體例子,后面關(guān)于“查詢通道”和“領(lǐng)域?qū)ο髠}(cāng)庫(kù)”的博文會(huì)更具體的闡述)。我們可以看到,對(duì)于一些業(yè)務(wù)視圖,以及報(bào)表模塊,很明顯不適合使用面向?qū)ο蟮姆绞皆O(shè)計(jì),因?yàn)檫@些“視圖”和“報(bào)表”,本質(zhì)上就不是業(yè)務(wù)實(shí)體。


          • 同樣出于性能的考慮,在業(yè)務(wù)實(shí)體的實(shí)現(xiàn)邏輯中,某些操作不適合過度偏執(zhí)的使用面向?qū)ο蠓绞健@纾?#8220;訂單”的“新增訂單明細(xì)”(order.addOrderItem(orderItem))中,如果業(yè)務(wù)邏輯規(guī)定一張訂單中包含優(yōu)惠商品的明細(xì)數(shù)目不能超過20條,使用面向?qū)ο蟮姆绞剑托枰延唵沃械乃杏唵蚊骷?xì)全部加載,然后逐個(gè)明細(xì)判斷其對(duì)應(yīng)的商品是否優(yōu)惠商品,再統(tǒng)計(jì)出優(yōu)惠商品的數(shù)目,這樣很明顯是低效率和高開銷的,這里只需要使用Repository提供的一個(gè)統(tǒng)計(jì)方法,用一個(gè)結(jié)構(gòu)化查詢邏輯返回統(tǒng)計(jì)結(jié)果即可,而這就是非面向?qū)ο蟮姆绞健?/span>


          本博文給有志于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的讀者潑了一下冷水,提出一些“反模式”(Bitter),是為了讓讀者冷靜一下,在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)過程中作出更靈活和更合理的選擇。關(guān)于這方面的論述,筆者在這里淺嘗則止,限于水平、經(jīng)驗(yàn)和表達(dá)能力,不敢胡亂賣弄,建議讀者可以參考閱讀Martin Fowler的《Patterns of Enterprise Application Architecture》一書的相關(guān)觀點(diǎn)。

          posted @ 2010-06-26 17:47 Johnny.Liang 閱讀(4114) | 評(píng)論 (2)編輯 收藏

          上一篇文章作為一個(gè)引子,說明了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的優(yōu)勢(shì),從本篇文章開始,筆者將會(huì)結(jié)合自己的實(shí)際經(jīng)驗(yàn),談及領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的應(yīng)用。本篇文章主要討論一下我們經(jīng)常會(huì)用到的一些對(duì)象:VODTODOPO

          由于不同的項(xiàng)目和開發(fā)人員有不同的命名習(xí)慣,這里我首先對(duì)上述的概念進(jìn)行一個(gè)簡(jiǎn)單描述,名字只是個(gè)標(biāo)識(shí),我們重點(diǎn)關(guān)注其概念:

           

          概念:

          VOView Object):視圖對(duì)象,用于展示層,它的作用是把某個(gè)指定頁(yè)面(或組件)的所有數(shù)據(jù)封裝起來。

          DTOData Transfer Object):數(shù)據(jù)傳輸對(duì)象,這個(gè)概念來源于J2EE的設(shè)計(jì)模式,原來的目的是為了EJB的分布式應(yīng)用提供粗粒度的數(shù)據(jù)實(shí)體,以減少分布式調(diào)用的次數(shù),從而提高分布式調(diào)用的性能和降低網(wǎng)絡(luò)負(fù)載,但在這里,我泛指用于展示層與服務(wù)層之間的數(shù)據(jù)傳輸對(duì)象。

          DODomain Object):領(lǐng)域?qū)ο螅褪菑默F(xiàn)實(shí)世界中抽象出來的有形或無形的業(yè)務(wù)實(shí)體。

          POPersistent Object):持久化對(duì)象,它跟持久層(通常是關(guān)系型數(shù)據(jù)庫(kù))的數(shù)據(jù)結(jié)構(gòu)形成一一對(duì)應(yīng)的映射關(guān)系,如果持久層是關(guān)系型數(shù)據(jù)庫(kù),那么,數(shù)據(jù)表中的每個(gè)字段(或若干個(gè))就對(duì)應(yīng)PO的一個(gè)(或若干個(gè))屬性。

           

          模型:

                 下面以一個(gè)時(shí)序圖建立簡(jiǎn)單模型來描述上述對(duì)象在三層架構(gòu)應(yīng)用中的位置


           

                

          l         用戶發(fā)出請(qǐng)求(可能是填寫表單),表單的數(shù)據(jù)在展示層被匹配為VO

          l         展示層把VO轉(zhuǎn)換為服務(wù)層對(duì)應(yīng)方法所要求的DTO,傳送給服務(wù)層。

          l         服務(wù)層首先根據(jù)DTO的數(shù)據(jù)構(gòu)造(或重建)一個(gè)DO,調(diào)用DO的業(yè)務(wù)方法完成具體業(yè)務(wù)。

          l         服務(wù)層把DO轉(zhuǎn)換為持久層對(duì)應(yīng)的PO(可以使用ORM工具,也可以不用),調(diào)用持久層的持久化方法,把PO傳遞給它,完成持久化操作。

          l         對(duì)于一個(gè)逆向操作,如讀取數(shù)據(jù),也是用類似的方式轉(zhuǎn)換和傳遞,略。

           

          VODTO的區(qū)別

                 大家可能會(huì)有個(gè)疑問(在筆者參與的項(xiàng)目中,很多程序員也有相同的疑惑):既然DTO是展示層與服務(wù)層之間傳遞數(shù)據(jù)的對(duì)象,為什么還需要一個(gè)VO呢?對(duì)!對(duì)于絕大部分的應(yīng)用場(chǎng)景來說,DTOVO的屬性值基本是一致的,而且他們通常都是POJO,因此沒必要多此一舉,但不要忘記這是實(shí)現(xiàn)層面的思維,對(duì)于設(shè)計(jì)層面來說,概念上還是應(yīng)該存在VODTO,因?yàn)閮烧哂兄举|(zhì)的區(qū)別,DTO代表服務(wù)層需要接收的數(shù)據(jù)和返回的數(shù)據(jù),而VO代表展示層需要顯示的數(shù)據(jù)。

                 用一個(gè)例子來說明可能會(huì)比較容易理解:例如服務(wù)層有一個(gè)getUser的方法返回一個(gè)系統(tǒng)用戶,其中有一個(gè)屬性是gender(性別),對(duì)于服務(wù)層來說,它只從語(yǔ)義上定義:1-男性,2-女性,0-未指定,而對(duì)于展示層來說,它可能需要用“帥哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。說到這里,可能你還會(huì)反駁,在服務(wù)層直接就返回“帥哥美女”不就行了嗎?對(duì)于大部分應(yīng)用來說,這不是問題,但設(shè)想一下,如果需求允許客戶可以定制風(fēng)格,而不同風(fēng)格對(duì)于“性別”的表現(xiàn)方式不一樣,又或者這個(gè)服務(wù)同時(shí)供多個(gè)客戶端使用(不同門戶),而不同的客戶端對(duì)于表現(xiàn)層的要求有所不同,那么,問題就來了。再者,回到設(shè)計(jì)層面上分析,從職責(zé)單一原則來看,服務(wù)層只負(fù)責(zé)業(yè)務(wù),與具體的表現(xiàn)形式無關(guān),因此,它返回的DTO,不應(yīng)該出現(xiàn)與表現(xiàn)形式的耦合。

                 理論歸理論,這到底還是分析設(shè)計(jì)層面的思維,是否在實(shí)現(xiàn)層面必須這樣做呢?一刀切的做法往往會(huì)得不償失,下面我馬上會(huì)分析應(yīng)用中如何做出正確的選擇。

           

          VODTO的應(yīng)用

                 上面只是用了一個(gè)簡(jiǎn)單的例子來說明VODTO在概念上的區(qū)別,本節(jié)將會(huì)告訴你如何在應(yīng)用中做出正確的選擇。

                 在以下才場(chǎng)景中,我們可以考慮把VODTO二合為一(注意:是實(shí)現(xiàn)層面):

          l         當(dāng)需求非常清晰穩(wěn)定,而且客戶端很明確只有一個(gè)的時(shí)候,沒有必要把VODTO區(qū)分開來,這時(shí)候VO可以退隱,用一個(gè)DTO即可,為什么是VO退隱而不是DTO?回到設(shè)計(jì)層面,服務(wù)層的職責(zé)依然不應(yīng)該與展示層耦合,所以,對(duì)于前面的例子,你很容易理解,DTO對(duì)于“性別”來說,依然不能用“帥哥美女”,這個(gè)轉(zhuǎn)換應(yīng)該依賴于頁(yè)面的腳本(如JavaScript)或其他機(jī)制(JSTLELCSS

          l         即使客戶端可以進(jìn)行定制,或者存在多個(gè)不同的客戶端,如果客戶端能夠用某種技術(shù)(腳本或其他機(jī)制)實(shí)現(xiàn)轉(zhuǎn)換,同樣可以讓VO退隱

           

          以下場(chǎng)景需要優(yōu)先考慮VODTO并存:

          l         上述場(chǎng)景的反面場(chǎng)景

          l         因?yàn)槟撤N技術(shù)原因,比如某個(gè)框架(如Flex)提供自動(dòng)把POJO轉(zhuǎn)換為UI中某些Field時(shí),可以考慮在實(shí)現(xiàn)層面定義出VO,這個(gè)權(quán)衡完全取決于使用框架的自動(dòng)轉(zhuǎn)換能力帶來的開發(fā)和維護(hù)效率提升與設(shè)計(jì)多一個(gè)VO所多做的事情帶來的開發(fā)和維護(hù)效率的下降之間的比對(duì)。

          l         如果頁(yè)面出現(xiàn)一個(gè)“大視圖”,而組成這個(gè)大視圖的所有數(shù)據(jù)需要調(diào)用多個(gè)服務(wù),返回多個(gè)DTO來組裝(當(dāng)然,這同樣可以通過服務(wù)層提供一次性返回一個(gè)大視圖的DTO來取代,但在服務(wù)層提供一個(gè)這樣的方法是否合適,需要在設(shè)計(jì)層面進(jìn)行權(quán)衡)。

           

          DTODO的區(qū)別

                 首先是概念上的區(qū)別,DTO是展示層和服務(wù)層之間的數(shù)據(jù)傳輸對(duì)象(可以認(rèn)為是兩者之間的協(xié)議),而DO是對(duì)現(xiàn)實(shí)世界各種業(yè)務(wù)角色的抽象,這就引出了兩者在數(shù)據(jù)上的區(qū)別,例如UserInfoUser(對(duì)于DTODO的命名規(guī)則,請(qǐng)參見筆者前面的一篇博文),對(duì)于一個(gè)getUser方法來說,本質(zhì)上它永遠(yuǎn)不應(yīng)該返回用戶的密碼,因此UserInfo至少比User少一個(gè)password的數(shù)據(jù)。而在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中,正如第一篇系列文章所說,DO不是簡(jiǎn)單的POJO,它具有領(lǐng)域業(yè)務(wù)邏輯。

           

          DTODO的應(yīng)用

                 從上一節(jié)的例子中,細(xì)心的讀者可能會(huì)發(fā)現(xiàn)問題:既然getUser方法返回的UserInfo不應(yīng)該包含password,那么就不應(yīng)該存在password這個(gè)屬性定義,但如果同時(shí)有一個(gè)createUser的方法,傳入的UserInfo需要包含用戶的password,怎么辦?在設(shè)計(jì)層面,展示層向服務(wù)層傳遞的DTO與服務(wù)層返回給展示層的DTO在概念上是不同的,但在實(shí)現(xiàn)層面,我們通常很少會(huì)這樣做(定義兩個(gè)UserInfo,甚至更多),因?yàn)檫@樣做并不見得很明智,我們完全可以設(shè)計(jì)一個(gè)完全兼容的DTO,在服務(wù)層接收數(shù)據(jù)的時(shí)候,不該由展示層設(shè)置的屬性(如訂單的總價(jià)應(yīng)該由其單價(jià)、數(shù)量、折扣等決定),無論展示層是否設(shè)置,服務(wù)層都一概忽略,而在服務(wù)層返回?cái)?shù)據(jù)時(shí),不該返回的數(shù)據(jù)(如用戶密碼),就不設(shè)置對(duì)應(yīng)的屬性。

                 對(duì)于DO來說,還有一點(diǎn)需要說明:為什么不在服務(wù)層中直接返回DO呢?這樣可以省去DTO的編碼和轉(zhuǎn)換工作,原因如下:

          l         兩者在本質(zhì)上的區(qū)別可能導(dǎo)致彼此并不一一對(duì)應(yīng),一個(gè)DTO可能對(duì)應(yīng)多個(gè)DO,反之亦然,甚至兩者存在多對(duì)多的關(guān)系。

          l         DO具有一些不應(yīng)該讓展示層知道的數(shù)據(jù)

          l         DO具有業(yè)務(wù)方法,如果直接把DO傳遞給展示層,展示層的代碼就可以繞過服務(wù)層直接調(diào)用它不應(yīng)該訪問的操作,對(duì)于基于AOP攔截服務(wù)層來進(jìn)行訪問控制的機(jī)制來說,這問題尤為突出,而在展示層調(diào)用DO的業(yè)務(wù)方法也會(huì)因?yàn)槭聞?wù)的問題,讓事務(wù)難以控制。

          l         對(duì)于某些ORM框架(如Hibernate)來說,通常會(huì)使用“延遲加載”技術(shù),如果直接把DO暴露給展示層,對(duì)于大部分情況,展示層不在事務(wù)范圍之內(nèi)(Open session in view在大部分情況下不是一種值得推崇的設(shè)計(jì)),如果其嘗試在Session關(guān)閉的情況下獲取一個(gè)未加載的關(guān)聯(lián)對(duì)象,會(huì)出現(xiàn)運(yùn)行時(shí)異常(對(duì)于Hibernate來說,就是LazyInitiliaztionException)。

          l         從設(shè)計(jì)層面來說,展示層依賴于服務(wù)層,服務(wù)層依賴于領(lǐng)域?qū)樱绻?/span>DO暴露出去,就會(huì)導(dǎo)致展示層直接依賴于領(lǐng)域?qū)樱@雖然依然是單向依賴,但這種跨層依賴會(huì)導(dǎo)致不必要的耦合。

           

          對(duì)于DTO來說,也有一點(diǎn)必須進(jìn)行說明,就是DTO應(yīng)該是一個(gè)“扁平的二維對(duì)象”,舉個(gè)例子來說明:如果User會(huì)關(guān)聯(lián)若干個(gè)其他實(shí)體(例如AddressAccountRegion等),那么getUser()返回的UserInfo,是否就需要把其關(guān)聯(lián)的對(duì)象的DTO都一并返回呢?如果這樣的話,必然導(dǎo)致數(shù)據(jù)傳輸量的大增,對(duì)于分布式應(yīng)用來說,由于涉及數(shù)據(jù)在網(wǎng)絡(luò)上的傳輸、序列化和反序列化,這種設(shè)計(jì)更不可接受。如果getUser除了要返回User的基本信息外,還需要返回一個(gè)AccountIdAccountNameRegionIdRegionName,那么,請(qǐng)把這些屬性定義到UserInfo中,把一個(gè)“立體”的對(duì)象樹“壓扁”成一個(gè)“扁平的二維對(duì)象”,筆者目前參與的項(xiàng)目是一個(gè)分布式系統(tǒng),該系統(tǒng)不管三七二十一,把一個(gè)對(duì)象的所有關(guān)聯(lián)對(duì)象都轉(zhuǎn)換為相同結(jié)構(gòu)的DTO對(duì)象樹并返回,導(dǎo)致性能非常的慢。

           

           

          DOPO的區(qū)別

                 DOPO在絕大部分情況下是一一對(duì)應(yīng)的,PO是只含有get/set方法的POJO,但某些場(chǎng)景還是能反映出兩者在概念上存在本質(zhì)的區(qū)別:

          l         DO在某些場(chǎng)景下不需要進(jìn)行顯式的持久化,例如利用策略模式設(shè)計(jì)的商品折扣策略,會(huì)衍生出折扣策略的接口和不同折扣策略實(shí)現(xiàn)類,這些折扣策略實(shí)現(xiàn)類可以算是DO,但它們只駐留在靜態(tài)內(nèi)存,不需要持久化到持久層,因此,這類DO是不存在對(duì)應(yīng)的PO的。

          l         同樣的道理,某些場(chǎng)景下,PO也沒有對(duì)應(yīng)的DO,例如老師Teacher和學(xué)生Student存在多對(duì)多的關(guān)系,在關(guān)系數(shù)據(jù)庫(kù)中,這種關(guān)系需要表現(xiàn)為一個(gè)中間表,也就對(duì)應(yīng)有一個(gè)TeacherAndStudentPOPO,但這個(gè)PO在業(yè)務(wù)領(lǐng)域沒有任何現(xiàn)實(shí)的意義,它完全不能與任何DO對(duì)應(yīng)上。這里要特別聲明,并不是所有多對(duì)多關(guān)系都沒有業(yè)務(wù)含義,這跟具體業(yè)務(wù)場(chǎng)景有關(guān),例如:兩個(gè)PO之間的關(guān)系會(huì)影響具體業(yè)務(wù),并且這種關(guān)系存在多種類型,那么這種多對(duì)多關(guān)系也應(yīng)該表現(xiàn)為一個(gè)DO,又如:“角色”與“資源”之間存在多對(duì)多關(guān)系,而這種關(guān)系很明顯會(huì)表現(xiàn)為一個(gè)DO——“權(quán)限”。

          l         某些情況下,為了某種持久化策略或者性能的考慮,一個(gè)PO可能對(duì)應(yīng)多個(gè)DO,反之亦然。例如客戶Customer有其聯(lián)系信息Contacts,這里是兩個(gè)一對(duì)一關(guān)系的DO,但可能出于性能的考慮(極端情況,權(quán)作舉例),為了減少數(shù)據(jù)庫(kù)的連接查詢操作,把CustomerContacts兩個(gè)DO數(shù)據(jù)合并到一張數(shù)據(jù)表中。反過來,如果一本圖書Book,有一個(gè)屬性是封面cover,但該屬性是一副圖片的二進(jìn)制數(shù)據(jù),而某些查詢操作不希望把cover一并加載,從而減輕磁盤IO開銷,同時(shí)假設(shè)ORM框架不支持屬性級(jí)別的延遲加載,那么就需要考慮把cover獨(dú)立到一張數(shù)據(jù)表中去,這樣就形成一個(gè)DO對(duì)應(yīng)對(duì)個(gè)PO的情況。

          l         PO的某些屬性值對(duì)于DO沒有任何意義,這些屬性值可能是為了解決某些持久化策略而存在的數(shù)據(jù),例如為了實(shí)現(xiàn)“樂觀鎖”,PO存在一個(gè)version的屬性,這個(gè)version對(duì)于DO來說是沒有任何業(yè)務(wù)意義的,它不應(yīng)該在DO中存在。同理,DO中也可能存在不需要持久化的屬性。

           

          DOPO的應(yīng)用

                 由于ORM框架的功能非常強(qiáng)大而大行其道,而且JavaEE也推出了JPA規(guī)范,現(xiàn)在的業(yè)務(wù)應(yīng)用開發(fā),基本上不需要區(qū)分DOPOPO完全可以通過JPAHibernate Annotations/hbm隱藏在DO之中。雖然如此,但有些問題我們還必須注意:

          l         對(duì)于DO中不需要持久化的屬性,需要通過ORM顯式的聲明,如:在JPA中,可以利用@Transient聲明。

          l         對(duì)于PO中為了某種持久化策略而存在的屬性,例如version,由于DOPO合并了,必須在DO中聲明,但由于這個(gè)屬性對(duì)DO是沒有任何業(yè)務(wù)意義的,需要讓該屬性對(duì)外隱藏起來,最常見的做法是把該屬性的get/set方法私有化,甚至不提供get/set方法,但對(duì)于Hibernate來說,這需要特別注意,由于Hibernate從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)轉(zhuǎn)換為DO時(shí),是利用反射機(jī)制先調(diào)用DO的空參數(shù)構(gòu)造函數(shù)構(gòu)造DO實(shí)例,然后再利用JavaBean的規(guī)范反射出set方法來為每個(gè)屬性設(shè)值,如果不顯式聲明set方法,或把set方法設(shè)置為private,都會(huì)導(dǎo)致Hibernate無法初始化DO,從而出現(xiàn)運(yùn)行時(shí)異常,可行的做法是把屬性的set方法設(shè)置為protected

          l         對(duì)于一個(gè)DO對(duì)應(yīng)多個(gè)PO,或者一個(gè)PO對(duì)應(yīng)多個(gè)DO的場(chǎng)景,以及屬性級(jí)別的延遲加載,Hibernate都提供了很好的支持,請(qǐng)參考Hibnate的相關(guān)資料。

           

           

              到目前為止,相信大家都已經(jīng)比較清晰的了解VODTODOPO的概念、區(qū)別和實(shí)際應(yīng)用了。通過上面的詳細(xì)分析,我們還可以總結(jié)出一個(gè)原則:分析設(shè)計(jì)層面和實(shí)現(xiàn)層面完全是兩個(gè)獨(dú)立的層面,即使實(shí)現(xiàn)層面通過某種技術(shù)手段可以把兩個(gè)完全獨(dú)立的概念合二為一,在分析設(shè)計(jì)層面,我們?nèi)匀唬ㄖ辽僭陬^腦中)需要把概念上獨(dú)立的東西清晰的區(qū)分開來,這個(gè)原則對(duì)于做好分析設(shè)計(jì)非常重要(工具越先進(jìn),往往會(huì)讓我們?cè)铰槟荆5谝黄盗胁┪膾伌u引玉,大唱領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的優(yōu)勢(shì),但其實(shí)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)在現(xiàn)實(shí)環(huán)境中還是有種種的限制,需要選擇性的使用,正如我在《田七的智慧》博文中提到,我們不能永遠(yuǎn)的理想化的去選擇所謂“最好的設(shè)計(jì)”,在必要的情況下,我們還是要敢于放棄,因?yàn)樽詈线m的設(shè)計(jì)才是最好的設(shè)計(jì)。本來,系列中的第二篇博文應(yīng)該是討論領(lǐng)取驅(qū)動(dòng)設(shè)計(jì)的限制和如何選擇性的使用,但請(qǐng)?jiān)徫业氖韬觯乱黄盗胁┪臅?huì)把這個(gè)主題補(bǔ)上,敬請(qǐng)關(guān)注。
          posted @ 2010-05-27 00:07 Johnny.Liang 閱讀(38271) | 評(píng)論 (16)編輯 收藏

          主站蜘蛛池模板: 蕲春县| 黔东| 密山市| 德昌县| 黄石市| 河南省| 辽中县| 来凤县| 酉阳| 绿春县| 新闻| 玛多县| 大安市| 育儿| 湘潭市| 英山县| 庆元县| 高清| 蒙山县| 二连浩特市| 贵港市| 抚松县| 泸水县| 宁阳县| 耿马| 高邮市| 天气| 清流县| 合作市| 公主岭市| 刚察县| 丰顺县| 扎囊县| 南涧| 宝坻区| 徐水县| 望奎县| 兴化市| 饶河县| 乡宁县| 澳门|