VO、DTO、BO、DO、PO、POJO、Entity的概念、区别和应用
一、概念
-  
VO(View Object):視圖層,用于展示層,它的作用是把某個指定頁面(或組件)的所有數據封裝起來。
 -  
DTO(Data Transfer Object):數據傳輸對象,這個概念來源于J2EE的設計模式,原來的目的是為了EJB的分布式應用提供粗粒度的數據實體,以減少分布式調用的次數,從而提高分布式調用的性能和降低網絡負載,但在這里,我泛指用于展示層與服務層之間的數據傳輸對象。
 -  
DO(Domain Object):領域對象,就是從現實世界中抽象出來的有形或無形的業務實體。
 -  
PO(Persistent Object):持久化對象(例如:entity、bean),他跟持久層(通常是關系型數據庫)的數據結構形成一一對應的映射關系,如果持久層是關系型數據庫,那么,數據表中的每個字段(或若干個)就對應PO的一個(或若干個)屬性。
 
下面以一個時序圖建立簡單模型來描述上述對象在三層架構應用中的位置:
流程圖解釋:
對于一個逆向操作,如讀取數據,也是用類似的方式轉換和傳遞。
VO與DTO的區別
大家可能會有個疑問(在筆者參與的項目中,很多程序員也有相同的疑惑):既然DTO是展示層與服務層之間傳遞數據的對象,為什么還需要一個VO呢?對!對于絕大部分的應用場景來說,DTO和VO的屬性值基本是一致的,而且他們通常都是POJO,因此沒必要多此一舉,但不要忘記這是實現層面的思維,對于設計層面來說,概念上還是應該存在VO和DTO,因為兩者有著本質的區別,DTO代表服務層需要接收的數據和返回的數據,而VO代表展示層需要顯示的數據。
用一個例子來說明可能會比較容易理解:例如服務層有一個getUser的方法返回一個系統用戶,其中有一個屬性是gender(性別),對于服務層來說,它只從語義上定義:1-男性,2-女性,0-未指定,而對于展示層來說,它可能需要用“帥哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。說到這里,可能你還會反駁,在服務層直接就返回“帥哥美女”不就行了嗎?對于大部分應用來說,這不是問題,但設想一下,如果需求允許客戶可以定制風格,而不同風格對于“性別”的表現方式不一樣,又或者這個服務同時供多個客戶端使用(不同門戶),而不同的客戶端對于表現層的要求有所不同,那么,問題就來了。再者,回到設計層面上分析,從職責單一原則來看,服務層只負責業務,與具體的表現形式無關,因此,它返回的DTO,不應該出現與表現形式的耦合。
理論歸理論,這到底還是分析設計層面的思維,是否在實現層面必須這樣做呢?一刀切的做法往往會得不償失,下面我馬上會分析應用中如何做出正確的選擇。
VO與DTO的應用
上面只是用了一個簡單的例子來說明VO與DTO在概念上的區別,本節將會告訴你如何在應用中做出正確的選擇。
- 在以下才場景中,我們可以考慮把VO與DTO二合為一(注意:是實現層面):
 
當需求非常清晰穩定,而且客戶端很明確只有一個的時候,沒有必要把VO和DTO區分開來,這時候VO可以退隱,用一個DTO即可,為什么是VO退隱而不是DTO?回到設計層面,服務層的職責依然不因該與展示層耦合,所以,對于前面的例子,你很容易理解,DTO對于“性別”來說,依然不能用“帥哥美女”,這個轉換應該依賴與頁面的腳本(如javaScript)或其他機制(JSTL,EL,CSS)。
 即使客戶端可以進行定制,或者存在多個不同的客戶端,如果客戶端能夠用某種技術(腳本或其他機制)實現轉換,同樣可以讓VO隱退。
- 以下場景需要優先考慮VO,DTO并存:
 
上述場景的反面場景
 因為某種技術原因,比如某個框架(如Flex)提供自動把POJO轉換為UI中某些FieId時,可以考慮在實現層面定義出VO,這個這個權衡完全取決于使用框架的自動轉換能力帶來的開發和維護效率提升與設計多一個VO所多做的事情帶來的開發和維護效率的下降之間的對比。
 如果頁面出現一個“大視圖”,而組成這個大視圖的所有數據需要調用多個服務,返回多個DTO來組裝(當然,這同樣可以通過服務層提供一次性返回一個大視圖的DTO來取代,但在服務層提供一個這樣的方法是否合適,需要在設計層面進行權衡)。
DTO與DO的區別
首先是概念上的區別,DTO是展示層和服務層之間的數據傳輸對象(可以認為是兩者之間的協議),而DO是對現實世界各種業務角色的抽象,這就引出了兩者在數據上的區別,例如UserInfo和User(對于DTO和DO的命名規則,請參見筆者前面的一篇博文),對于一個getUser方法來說,本質上它永遠不應該返回用戶的密碼,因此UserInfo至少比User少一個password的數據。而在領域驅動設計中,正如第一篇系列文章所說,DO不是簡單的POJO,它具有領域業務邏輯。
DTO與DO的應用
-  
兩者在本質上的區別可能導致彼此并不一一對應,一個DTO可能對應多個DO,反之亦然,甚至兩者存在多對多的關系。
 -  
DO具有一些不應該讓展示層知道的數據。
 -  
DO具有業務方法,如果直接把DO傳遞給展示層,展示層的代碼就可以繞過服務層直接調用它不應該訪問的操作,對于基于AOP攔截服務層來進行訪問控制的機制來說,這問題尤為突出,而在展示層調用DO的業務方法也會因為事務的問題,讓事務難以控制。
 -  
對于某些ORM框架(如Hibernate)來說,通常會使用“延遲加載”技術,如果直接把DO暴露給展示層,對于大部分情況,展示層不在事務范圍之內(Open session in view在大部分情況下不是一種值得推崇的設計),如果其嘗試在Session關閉的情況下獲取一個未加載的關聯對象,會出現運行時異常(對于Hibernate來說,就是LazyInitiliaztionException)。
 -  
從設計層面來說,展示層依賴于服務層,服務層依賴于領域層,如果把DO暴露出去,就會導致展示層直接依賴于領域層,這雖然依然是單向依賴,但這種跨層依賴會導致不必要的耦合。
 -  
對于DTO來說,也有一點必須進行說明,就是DTO應該是一個“扁平的二維對象”,舉個例子來說明:如果User會關聯若干個其他實體(例如Address、Account、Region等),那么getUser()返回的UserInfo,是否就需要把其關聯的對象的DTO都一并返回呢?如果這樣的話,必然導致數據傳輸量的大增,對于分布式應用來說,由于涉及數據在網絡上的傳輸、序列化和反序列化,這種設計更不可接受。如果getUser除了要返回User的基本信息外,還需要返回一個AccountId、AccountName、RegionId、RegionName,那么,請把這些屬性定義到UserInfo中,把一個“立體”的對象樹“壓扁”成一個“扁平的二維對象”,筆者目前參與的項目是一個分布式系統,該系統不管三七二十一,把一個對象的所有關聯對象都轉換為相同結構的DTO對象樹并返回,導致性能非常的慢。
 
DO與PO的區別
DO和PO在絕大部分情況下是一一對應的,PO是只含有get/set方法的POJO,但某些場景還是能反映出兩者在概念上存在本質的區別:
DO在某些場景下不需要進行顯式的持久化,例如利用策略模式設計的商品折扣策略,會衍生出折扣策略的接口和不同折扣策略實現類,這些折扣策略實現類可以算是DO,但它們只駐留在靜態內存,不需要持久化到持久層,因此,這類DO是不存在對應的PO的。
 同樣的道理,某些場景下,PO也沒有對應的DO,例如老師Teacher和學生Student存在多對多的關系,在關系數據庫中,這種關系需要表現為一個中間表,也就對應有一個TeacherAndStudentPO的PO,但這個PO在業務領域沒有任何現實的意義,它完全不能與任何DO對應上。這里要特別聲明,并不是所有多對多關系都沒有業務含義,這跟具體業務場景有關,例如:兩個PO之間的關系會影響具體業務,并且這種關系存在多種類型,那么這種多對多關系也應該表現為一個DO,又如:“角色”與“資源”之間存在多對多關系,而這種關系很明顯會表現為一個DO——“權限”。
 某些情況下,為了某種持久化策略或者性能的考慮,一個PO可能對應多個DO,反之亦然。例如客戶Customer有其聯系信息Contacts,這里是兩個一對一關系的DO,但可能出于性能的考慮(極端情況,權作舉例),為了減少數據庫的連接查詢操作,把Customer和Contacts兩個DO數據合并到一張數據表中。反過來,如果一本圖書Book,有一個屬性是封面cover,但該屬性是一副圖片的二進制數據,而某些查詢操作不希望把cover一并加載,從而減輕磁盤IO開銷,同時假設ORM框架不支持屬性級別的延遲加載,那么就需要考慮把cover獨立到一張數據表中去,這樣就形成一個DO對應對個PO的情況。
 PO的某些屬性值對于DO沒有任何意義,這些屬性值可能是為了解決某些持久化策略而存在的數據,例如為了實現“樂觀鎖”,PO存在一個version的屬性,這個version對于DO來說是沒有任何業務意義的,它不應該在DO中存在。同理,DO中也可能存在不需要持久化的屬性。
DO與PO的應用
由于ORM框架的功能非常強大而大行其道,而且JavaEE也推出了JPA規范,現在的業務應用開發,基本上不需要區分DO與PO,PO完全可以通過JPA,Hibernate Annotations/hbm隱藏在DO之中。雖然如此,但有些問題我們還必須注意:
對于DO中不需要持久化的屬性,需要通過ORM顯式的聲明,如:在JPA中,可以利用@Transient聲明。
 對于PO中為了某種持久化策略而存在的屬性,例如version,由于DO、PO合并了,必須在DO中聲明,但由于這個屬性對DO是沒有任何業務意義的,需要讓該屬性對外隱藏起來,最常見的做法是把該屬性的get/set方法私有化,甚至不提供get/set方法,但對于Hibernate來說,這需要特別注意,由于Hibernate從數據庫讀取數據轉換為DO時,是利用反射機制先調用DO的空參數構造函數構造DO實例,然后再利用JavaBean的規范反射出set方法來為每個屬性設值,如果不顯式聲明set方法,或把set方法設置為private,都會導致Hibernate無法初始化DO,從而出現運行時異常,可行的做法是把屬性的set方法設置為protected。
 對于一個DO對應多個PO,或者一個PO對應多個DO的場景,以及屬性級別的延遲加載,Hibernate都提供了很好的支持,請參考Hibnate的相關資料。
二、詳述 PO VO BO DTO DAO 和 POJO 的概念及區別
說實話,我相信對于剛接觸 PO、VO、BO、DTO、DAO 和 POJO 這些概念的同學來說,大都會有一種“這都是什么鬼?”的感覺,可謂是云里霧里,不知今夕何夕!現在,就讓咱們一起揭開這些 “X”O 的面紗,看看它們的廬山真面目。首先,來個圖瞅瞅:
第 1 個:DAO
DAO(Data Access Object)數據訪問對象,它是一個面向對象的數據庫接口,負責持久層的操作,為業務層提供接口,主要用來封裝對數據庫的訪問,常見操作無外乎 CURD。我們也可以認為一個 DAO 對應一個 POJO 的對象,它位于業務邏輯與數據庫資源中間,可以結合 PO 對數據庫進行相關的操作。
第 2 個:PO
PO(Persistent Object)持久層對象,它是由一組屬性和屬性的get和set方法組成,最簡單的 PO 就是對應數據庫中某個表中的一條記錄(也就是說,我們可以將數據庫表中的一條記錄理解為一個持久層對象),多個記錄可以用 PO 的集合,PO 中應該不包含任何對數據庫的操作。PO 的屬性是跟數據庫表的字段一一對應的,此外 PO 對象需要實現序列化接口。
第 3 個:BO
BO(Business Object)業務層對象,是簡單的真實世界的軟件抽象,通常位于中間層。BO 的主要作用是把業務邏輯封裝為一個對象,這個對象可以包括一個或多個其它的對象。舉一個求職簡歷的例子,每份簡歷都包括教育經歷、項目經歷等,我們可以讓教育經歷和項目經歷分別對應一個 PO,這樣在我們建立對應求職簡歷的 BO 對象處理簡歷的時候,讓每個 BO 都包含這些 PO 即可。
第 4 個:VO
VO(Value Object)值對象,通常用于業務層之間的數據傳遞,和 PO 一樣也是僅僅包含數據而已,但 VO 應該是抽象出的業務對象,可以和表對應,也可以不對應,這根據業務的需要。 如果鍋碗瓢盆分別為對應的業務對象的話,那么整個碗柜就是一個值對象。此外,VO 也可以稱為頁面對象,如果稱為頁面對象的話,那么它所代表的將是整個頁面展示層的對象,也可以由需要的業務對象進行組裝而來。
第 5 個:DTO
DTO(Data Transfer Object)數據傳輸對象,主要用于遠程調用等需要大量傳輸對象的地方,比如我們有一個交易訂單表,含有 25 個字段,那么其對應的 PO 就有 25 個屬性,但我們的頁面上只需要顯示 5 個字段,因此沒有必要把整個 PO 對象傳遞給客戶端,這時我們只需把僅有 5 個屬性的 DTO 把結果傳遞給客戶端即可,而且如果用這個對象來對應界面的顯示對象,那此時它的身份就轉為 VO。使用 DTO 的好處有兩個,一是能避免傳遞過多的無用數據,提高數據的傳輸速度;二是能隱藏后端的表結構。常見的用法是:將請求的數據或屬性組裝成一個 RequestDTO,再將響應的數據或屬性組裝成一個 ResponseDTO.
第 6 個:POJO
POJO(Plain Ordinary Java Object)簡單的 Java 對象,實際就是普通的 JavaBeans,是為了避免和 EJB(Enterprise JavaBean)混淆所創造的簡稱。POJO 實質上可以理解為簡單的實體類,其中有一些屬性及其getter和setter方法的類,沒有業務邏輯,也不允許有業務方法,也不能攜帶有connection之類的方法。POJO 是 JavaEE 世界里面最靈活的對象,在簡單系統中,如果從數據庫到頁面展示都是 POJO 的話,它可以是 DTO;如果從數據庫中到業務處理中都是 POJO 的話,它可以是 BO;如果從數據庫到整個頁面的展示的話,它也可以是 VO.
擴展閱讀:
在實際的項目中,我們還會遇到一個常見的對象,那就是 Entity 實體對象,它對應數據庫中的表,我們可以簡單的理解為一個表對應一個 Entity,同樣以交易訂單表 Order 為例,如果這個表有 25 個字段,那么這個 OrderEntity 對象里面也要含有 25 個對應的屬性。
總結
以上是生活随笔為你收集整理的VO、DTO、BO、DO、PO、POJO、Entity的概念、区别和应用的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: MATLAB做驻波,SMB色谱分离驻波优
 - 下一篇: ad中按钮开关的符号_送给初学者电工最常