淺析 VO、DTO、DO、PO 的概念、區(qū)別和用處!
本文來(lái)源:http://i7q.cn/55FFFq
概念:
VO(View Object):視圖對(duì)象,用于展示層,它的作用是把某個(gè)指定頁(yè)面(或組件)的所有數(shù)據(jù)封裝起來(lái)。 DTO(Data Transfer Object):數(shù)據(jù)傳輸對(duì)象,這個(gè)概念來(lái)源于J2EE的設(shè)計(jì)模式,原來(lái)的目的是為了EJB的分布式應(yīng)用提供粗粒度的數(shù)據(jù)實(shí)體,以減少分布式調(diào)用的次數(shù),從而提高分布式調(diào)用的性能和降低網(wǎng)絡(luò)負(fù)載,但在這里,我泛指用于展示層與服務(wù)層之間的數(shù)據(jù)傳輸對(duì)象。 DO(Domain Object):領(lǐng)域?qū)ο?,就是從現(xiàn)實(shí)世界中抽象出來(lái)的有形或無(wú)形的業(yè)務(wù)實(shí)體。 PO(Persistent 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è))屬性。
模型:

用戶發(fā)出請(qǐng)求(可能是填寫(xiě)表單),表單的數(shù)據(jù)在展示層被匹配為VO。 展示層把VO轉(zhuǎn)換為服務(wù)層對(duì)應(yīng)方法所要求的DTO,傳送給服務(wù)層。 服務(wù)層首先根據(jù)DTO的數(shù)據(jù)構(gòu)造(或重建)一個(gè)DO,調(diào)用DO的業(yè)務(wù)方法完成具體業(yè)務(wù)。 服務(wù)層把DO轉(zhuǎn)換為持久層對(duì)應(yīng)的PO(可以使用ORM工具,也可以不用),調(diào)用持久層的持久化方法,把PO傳遞給它,完成持久化操作。 對(duì)于一個(gè)逆向操作,如讀取數(shù)據(jù),也是用類(lèi)似的方式轉(zhuǎn)換和傳遞,略。
VO與DTO的區(qū)別
VO與DTO的應(yīng)用
需求非常清晰穩(wěn)定,而且客戶端很明確只有一個(gè)的時(shí)候,沒(méi)有必要把VO和DTO區(qū)分開(kāi)來(lái),這時(shí)候VO可以退隱,用一個(gè)DTO即可,為什么是VO退隱而不是DTO?回到設(shè)計(jì)層面,服務(wù)層的職責(zé)依然不應(yīng)該與展示層耦合,所以,對(duì)于前面的例子,你很容易理解,DTO對(duì)于“性別”來(lái)說(shuō),依然不能用“帥哥美女”,這個(gè)轉(zhuǎn)換應(yīng)該依賴于頁(yè)面的腳本(如JavaScript)或其他機(jī)制(JSTL、EL、CSS) 即使客戶端可以進(jìn)行定制,或者存在多個(gè)不同的客戶端,如果客戶端能夠用某種技術(shù)(腳本或其他機(jī)制)實(shí)現(xiàn)轉(zhuǎn)換,同樣可以讓VO退隱
上述場(chǎng)景的反面場(chǎng)景 因?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)換能力帶來(lái)的開(kāi)發(fā)和維護(hù)效率提升與設(shè)計(jì)多一個(gè)VO所多做的事情帶來(lái)的開(kāi)發(fā)和維護(hù)效率的下降之間的比對(duì)。 如果頁(yè)面出現(xiàn)一個(gè)“大視圖”,而組成這個(gè)大視圖的所有數(shù)據(jù)需要調(diào)用多個(gè)服務(wù),返回多個(gè)DTO來(lái)組裝(當(dāng)然,這同樣可以通過(guò)服務(wù)層提供一次性返回一個(gè)大視圖的DTO來(lái)取代,但在服務(wù)層提供一個(gè)這樣的方法是否合適,需要在設(shè)計(jì)層面進(jìn)行權(quán)衡)。
DTO與DO的區(qū)別
https://www.cnblogs.com/qixuejia/p/10789612.html
DTO與DO的應(yīng)用
兩者在本質(zhì)上的區(qū)別可能導(dǎo)致彼此并不一一對(duì)應(yīng),一個(gè)DTO可能對(duì)應(yīng)多個(gè)DO,反之亦然,甚至兩者存在多對(duì)多的關(guān)系。 DO具有一些不應(yīng)該讓展示層知道的數(shù)據(jù) DO具有業(yè)務(wù)方法,如果直接把DO傳遞給展示層,展示層的代碼就可以繞過(guò)服務(wù)層直接調(diào)用它不應(yīng)該訪問(wèn)的操作,對(duì)于基于AOP攔截服務(wù)層來(lái)進(jìn)行訪問(wèn)控制的機(jī)制來(lái)說(shuō),這問(wèn)題尤為突出,而在展示層調(diào)用DO的業(yè)務(wù)方法也會(huì)因?yàn)槭聞?wù)的問(wèn)題,讓事務(wù)難以控制。 對(duì)于某些ORM框架(如Hibernate)來(lái)說(shuō),通常會(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來(lái)說(shuō),就是LazyInitiliaztionException)。 從設(shè)計(jì)層面來(lái)說(shuō),展示層依賴于服務(wù)層,服務(wù)層依賴于領(lǐng)域?qū)?,如果把DO暴露出去,就會(huì)導(dǎo)致展示層直接依賴于領(lǐng)域?qū)?,這雖然依然是單向依賴,但這種跨層依賴會(huì)導(dǎo)致不必要的耦合。
DO與PO的區(qū)別
DO在某些場(chǎng)景下不需要進(jìn)行顯式的持久化,例如利用策略模式設(shè)計(jì)的商品折扣策略,會(huì)衍生出折扣策略的接口和不同折扣策略實(shí)現(xiàn)類(lèi),這些折扣策略實(shí)現(xiàn)類(lèi)可以算是DO,但它們只駐留在靜態(tài)內(nèi)存,不需要持久化到持久層,因此,這類(lèi)DO是不存在對(duì)應(yīng)的PO的。 同樣的道理,某些場(chǎng)景下,PO也沒(méi)有對(duì)應(yīng)的DO,例如老師Teacher和學(xué)生Student存在多對(duì)多的關(guān)系,在關(guān)系數(shù)據(jù)庫(kù)中,這種關(guān)系需要表現(xiàn)為一個(gè)中間表,也就對(duì)應(yīng)有一個(gè)TeacherAndStudentPO的PO,但這個(gè)PO在業(yè)務(wù)領(lǐng)域沒(méi)有任何現(xiàn)實(shí)的意義,它完全不能與任何DO對(duì)應(yīng)上。這里要特別聲明,并不是所有多對(duì)多關(guān)系都沒(méi)有業(yè)務(wù)含義,這跟具體業(yè)務(wù)場(chǎng)景有關(guān),例如:兩個(gè)PO之間的關(guān)系會(huì)影響具體業(yè)務(wù),并且這種關(guān)系存在多種類(lèi)型,那么這種多對(duì)多關(guān)系也應(yīng)該表現(xiàn)為一個(gè)DO,又如:“角色”與“資源”之間存在多對(duì)多關(guān)系,而這種關(guān)系很明顯會(huì)表現(xiàn)為一個(gè)DO——“權(quán)限”。 某些情況下,為了某種持久化策略或者性能的考慮,一個(gè)PO可能對(duì)應(yīng)多個(gè)DO,反之亦然。例如客戶Customer有其聯(lián)系信息Contacts,這里是兩個(gè)一對(duì)一關(guān)系的DO,但可能出于性能的考慮(極端情況,權(quán)作舉例),為了減少數(shù)據(jù)庫(kù)的連接查詢操作,把Customer和Contacts兩個(gè)DO數(shù)據(jù)合并到一張數(shù)據(jù)表中。反過(guò)來(lái),如果一本圖書(shū)B(niǎo)ook,有一個(gè)屬性是封面cover,但該屬性是一副圖片的二進(jìn)制數(shù)據(jù),而某些查詢操作不希望把cover一并加載,從而減輕磁盤(pán)IO開(kāi)銷(xiāo),同時(shí)假設(shè)ORM框架不支持屬性級(jí)別的延遲加載,那么就需要考慮把cover獨(dú)立到一張數(shù)據(jù)表中去,這樣就形成一個(gè)DO對(duì)應(yīng)對(duì)個(gè)PO的情況。 PO的某些屬性值對(duì)于DO沒(méi)有任何意義,這些屬性值可能是為了解決某些持久化策略而存在的數(shù)據(jù),例如為了實(shí)現(xiàn)“樂(lè)觀鎖”,PO存在一個(gè)version的屬性,這個(gè)version對(duì)于DO來(lái)說(shuō)是沒(méi)有任何業(yè)務(wù)意義的,它不應(yīng)該在DO中存在。同理,DO中也可能存在不需要持久化的屬性。
DO與PO的應(yīng)用
由于ORM框架的功能非常強(qiáng)大而大行其道,而且JavaEE也推出了JPA規(guī)范,現(xiàn)在的業(yè)務(wù)應(yīng)用開(kāi)發(fā),基本上不需要區(qū)分DO與PO,PO完全可以通過(guò)JPA,Hibernate Annotations/hbm隱藏在DO之中。雖然如此,但有些問(wèn)題我們還必須注意: 對(duì)于DO中不需要持久化的屬性,需要通過(guò)ORM顯式的聲明,如:在JPA中,可以利用@Transient聲明。 對(duì)于PO中為了某種持久化策略而存在的屬性,例如version,由于DO、PO合并了,必須在DO中聲明,但由于這個(gè)屬性對(duì)DO是沒(méi)有任何業(yè)務(wù)意義的,需要讓該屬性對(duì)外隱藏起來(lái),最常見(jiàn)的做法是把該屬性的get/set方法私有化,甚至不提供get/set方法,但對(duì)于Hibernate來(lái)說(shuō),這需要特別注意,由于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方法來(lái)為每個(gè)屬性設(shè)值,如果不顯式聲明set方法,或把set方法設(shè)置為private,都會(huì)導(dǎo)致Hibernate無(wú)法初始化DO,從而出現(xiàn)運(yùn)行時(shí)異常,可行的做法是把屬性的set方法設(shè)置為protected。 對(duì)于一個(gè)DO對(duì)應(yīng)多個(gè)PO,或者一個(gè)PO對(duì)應(yīng)多個(gè)DO的場(chǎng)景,以及屬性級(jí)別的延遲加載,Hibernate都提供了很好的支持,請(qǐng)參考Hibnate的相關(guān)資料。
后臺(tái)回復(fù)?學(xué)習(xí)資料?領(lǐng)取學(xué)習(xí)視頻
如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝
評(píng)論
圖片
表情
