VO/DTO/DO/PO/POJO/BO/DAO 概念及區(qū)別

VO(Value Object) 值對(duì)象

通常用于業(yè)務(wù)層之間的數(shù)據(jù)傳遞,和 PO 一樣也是僅僅包含數(shù)據(jù)而已。但應(yīng)是抽象出的業(yè)務(wù)對(duì)象 , 可以和表對(duì)應(yīng) , 也可以不 , 這根據(jù)業(yè)務(wù)的需要 。用 new 關(guān)鍵字創(chuàng)建,由 GC 回收的。

VO(View Object)視圖對(duì)象

用于展示層,它的作用是把某個(gè)指定頁(yè)面(或組件)的所有數(shù)據(jù)封裝起來(lái)。

DTO(TO)(Data Transfer Object)數(shù)據(jù)傳輸對(duì)象

1 .用在需要跨進(jìn)程或遠(yuǎn)程傳輸時(shí),它不應(yīng)該包含業(yè)務(wù)邏輯。
2 .比如一張表有100個(gè)字段,那么對(duì)應(yīng)的PO就有100個(gè)屬性(大多數(shù)情況下,DTO 內(nèi)的數(shù)據(jù)來(lái)自多個(gè)表)。但view層只需顯示10個(gè)字段,沒有必要把整個(gè)PO對(duì)象傳遞到client,這時(shí)我們就可以用只有這10個(gè)屬性的DTO來(lái)傳輸數(shù)據(jù)到client,這樣也不會(huì)暴露server端表結(jié)構(gòu)。到達(dá)客戶端以后,如果用這個(gè)對(duì)象來(lái)對(duì)應(yīng)界面顯示,那此時(shí)它的身份就轉(zhuǎn)為VO。

DO(Domain Object)領(lǐng)域?qū)ο?/h2>

就是從現(xiàn)實(shí)世界中抽象出來(lái)的有形或無(wú)形的業(yè)務(wù)實(shí)體。

PO(persistant object) 持久對(duì)象

1 .有時(shí)也被稱為Data對(duì)象,對(duì)應(yīng)數(shù)據(jù)庫(kù)中的entity,可以簡(jiǎn)單認(rèn)為一個(gè)PO對(duì)應(yīng)數(shù)據(jù)庫(kù)中的一條記錄。
2 .在hibernate持久化框架中與insert/delet操作密切相關(guān)。
3 .PO中不應(yīng)該包含任何對(duì)數(shù)據(jù)庫(kù)的操作

POJO(plain ordinary java object) 無(wú)規(guī)則簡(jiǎn)單java對(duì)象

實(shí)際就是普通JavaBeans,使用POJO名稱是為了避免和EJB混淆起來(lái), 而且簡(jiǎn)稱比較直接. 其中有一些屬性及其getter setter方法的類,有時(shí)可以作為value object或dto(Data Transform Object)來(lái)使用.當(dāng)然,如果你有一個(gè)簡(jiǎn)單的運(yùn)算屬性也是可以的,但不允許有業(yè)務(wù)方法,也不能攜帶有connection之類的方法。

POJO是Plain Ordinary Java Objects的縮寫不錯(cuò),但是它通指沒有使用Entity Beans的普通java對(duì)象,可以把POJO作為支持業(yè)務(wù)邏輯的協(xié)助類。

BO(business object) 業(yè)務(wù)對(duì)象

從業(yè)務(wù)模型的角度看 , 見 UML 元件領(lǐng)域模型中的領(lǐng)域?qū)ο?。封裝業(yè)務(wù)邏輯的 java 對(duì)象 , 通過(guò)調(diào)用 DAO 方法 , 結(jié)合 PO,VO 進(jìn)行業(yè)務(wù)操作。 business object: 業(yè)務(wù)對(duì)象 主要作用是把業(yè)務(wù)邏輯封裝為一個(gè)對(duì)象。這個(gè)對(duì)象可以包括一個(gè)或多個(gè)其它的對(duì)象。 比如一個(gè)簡(jiǎn)歷,有教育經(jīng)歷、工作經(jīng)歷、社會(huì)關(guān)系等等。 我們可以把教育經(jīng)歷對(duì)應(yīng)一個(gè) PO ,工作經(jīng)歷對(duì)應(yīng)一個(gè) PO ,社會(huì)關(guān)系對(duì)應(yīng)一個(gè) PO 。 建立一個(gè)對(duì)應(yīng)簡(jiǎn)歷的 BO 對(duì)象處理簡(jiǎn)歷,每個(gè) BO 包含這些 PO 。 這樣處理業(yè)務(wù)邏輯時(shí),我們就可以針對(duì) BO 去處理。

DAO (data access object)數(shù)據(jù)訪問(wèn)對(duì)象

是一個(gè) sun 的一個(gè)標(biāo)準(zhǔn) j2ee 設(shè)計(jì)模式, 這個(gè)模式中有個(gè)接口就是 DAO ,它負(fù)持久層的操作。為業(yè)務(wù)層提供接口。此對(duì)象用于訪問(wèn)數(shù)據(jù)庫(kù)。通常和 PO 結(jié)合使用, DAO 中包含了各種數(shù)據(jù)庫(kù)的操作方法。通過(guò)它的方法 , 結(jié)合 PO 對(duì)數(shù)據(jù)庫(kù)進(jìn)行相關(guān)的操作。夾在業(yè)務(wù)邏輯與數(shù)據(jù)庫(kù)資源中間。配合 VO, 提供數(shù)據(jù)庫(kù)的 CRUD 操作.

VO與DTO的區(qū)別

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

用一個(gè)例子來(lái)說(shuō)明可能會(huì)比較容易理解:

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

VO與DTO的應(yīng)用

當(dāng)需求非常清晰穩(wěn)定,而且客戶端很明確只有一個(gè)的時(shí)候,沒有必要把VO和DTO區(qū)分開來(lái),這時(shí)候VO可以退隱,用一個(gè)DTO即可,為什么是VO退隱而不是DTO?回到設(shè)計(jì)層面,Service層的職責(zé)依然不應(yīng)該與View層耦合,所以,對(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)景需要優(yōu)先考慮VO、DTO并存:

因?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)的開發(fā)和維護(hù)效率提升與設(shè)計(jì)多一個(gè)VO所多做的事情帶來(lá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ū)別

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

DTO與DO的應(yīng)用

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

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

  • 兩者在本質(zhì)上的區(qū)別可能導(dǎo)致彼此并不一一對(duì)應(yīng),一個(gè)DTO可能對(duì)應(yīng)多個(gè)DO,反之亦然,甚至兩者存在多對(duì)多的關(guān)系。
  • DO具有一些不應(yīng)該讓View層知道的數(shù)據(jù) DO具有業(yè)務(wù)方法,如果直接把DO傳遞給View層,View層的代碼就可以繞過(guò)Service層直接調(diào)用它不應(yīng)該訪問(wèn)的操作,對(duì)于基于AOP攔截Service層來(lái)進(jìn)行訪問(wèn)控制的機(jī)制來(lái)說(shuō),這問(wèn)題尤為突出,而在View層調(diào)用DO的業(yè)務(wù)方法也會(huì)因?yàn)槭聞?wù)的問(wèn)題,讓事務(wù)難以控制。
  • 對(duì)于某些ORM框架(如hibernate)來(lái)說(shuō),通常會(huì)使用“延遲加載”技術(shù),如果直接把DO暴露給View層,對(duì)于大部分情況,View層不在事務(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ō),View層依賴于Service層,Service層依賴于領(lǐng)域?qū)樱绻袲O暴露出去,就會(huì)導(dǎo)致View層直接依賴于dao層,這雖然依然是單向依賴,但這種跨層依賴會(huì)導(dǎo)致不必要的耦合。
    對(duì)于DTO來(lái)說(shuō),也有一點(diǎn)必須進(jìn)行說(shuō)明,就是DTO應(yīng)該是一個(gè)“扁平的二維對(duì)象”

舉個(gè)例子來(lái)說(shuō)明:如果User會(huì)關(guān)聯(lián)若干個(gè)其他實(shí)體(例如Address、Account、Region等),那么getUser()返回的UserInfo,是否就需要把其關(guān)聯(lián)的對(duì)象的DTO都一并返回呢?如果這樣的話,必然導(dǎo)致數(shù)據(jù)傳輸量的大增,對(duì)于分布式應(yīng)用來(lái)說(shuō),由于涉及數(shù)據(jù)在網(wǎng)絡(luò)上的傳輸、序列化和反序列化,這種設(shè)計(jì)更不可接受。如果getUser除了要返回User的基本信息外,還需要返回一個(gè)AccountId、AccountName、RegionId、RegionName,那么,請(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)致性能非常的慢。

DO與PO的區(qū)別

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

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

同樣的道理,某些場(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è)TeacherAndStudentPO的PO,但這個(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)限”。

某些情況下,為了某種持久化策略或者性能的考慮,一個(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),如果一本圖書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的情況。

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

DO與PO的應(yīng)用

由于ORM框架的功能非常強(qiáng)大而大行其道,而且JavaEE也推出了JPA規(guī)范,現(xiàn)在的業(yè)務(wù)應(yīng)用開發(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是沒有任何業(yè)務(wù)意義的,需要讓該屬性對(duì)外隱藏起來(lái),最常見的做法是把該屬性的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)參考Hibernate的相關(guān)資料。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容