PO:
persistant object持久對象
最形象的了解就是一個PO就是資料庫中的一條記錄。
好處是可以把一條記錄作為一個對象處理,可以友善的轉為其它對象。
BO:
business object業務對象
主要作用是把業務邏輯封裝為一個對象。這個對象可以包括一個或多個其它的對象。
比如一個履歷,有教育經曆、工作經曆、社會關系等等。
我們可以把教育經曆對應一個PO,工作經曆對應一個PO,社會關系對應一個PO。
建立一個對應履歷的BO對象處理履歷,每個BO包含這些PO。
這樣處理業務邏輯時,我們就可以針對BO去處理。
VO :
value object值對象
ViewObject表現層對象
主要對應界面顯示的資料對象。對于一個WEB頁面,或者SWT、SWING的一個界面,用一個VO對象對應整個界面的值。
DTO :
Data Transfer Object資料傳輸對象
主要用于遠端調用等需要大量傳輸對象的地方。
比如我們一張表有100個字段,那麼對應的PO就有100個屬性。
但是我們界面上隻要顯示10個字段,
用戶端用WEB service來擷取資料,沒有必要把整個PO對象傳遞到用戶端,
這時我們就可以用隻有這10個屬性的DTO來傳遞結果到用戶端,這樣也不會暴露服務端表結構.到達用戶端以後,如果用這個對象來對應界面顯示,那此時它的身份就轉為VO
POJO :
plain ordinary java object 簡單java對象
個人感覺POJO是最常見最多變的對象,是一個中間對象,也是我們最常打交道的對象。
一個POJO持久化以後就是PO
直接用它傳遞、傳遞過程中就是DTO
直接用來對應表示層就是VO
POJO、PO、DTO、VO都是處理流程中的名字,不是PO對應一個POJO,DTO對應一個POJO,VO對應一個POJO
在有些情況下PO、DTO、VO是指同一個POJO
DAO:
data access object資料通路對象
這個大家最熟悉,和上面幾個O差別最大,基本沒有互相轉化的可能性和必要.
主要用來封裝對資料庫的通路。通過它可以把POJO持久化為PO,用PO組裝出來VO、DTO
總結下我認為一個對象究竟是什麼O要看具體環境,在不同的層、不同的應用場合,對象的身份也不一樣,而且對象身份的轉化也是很自然的。就像你對老婆來說就 是老公,對父母來說就是子女。設計這些概念的初衷不是為了唬人而是為了更好的了解和處理各種邏輯,讓大家能更好的去用面向對象的方式處理問題.
大家千萬不要陷入過度設計,大可不必為了設計而設計一定要在代碼中區分各個對象。一句話技術是為應用服務的。
歡迎指正。
畫了個圖,感覺沒有完全表達出自己的意思。。。。。誰幫忙完善下,最好能展現各個O在MVC中的位置
由于不同的項目和開發人員有不同的命名習慣,這裡我首先對上述的概念進行一個簡單描述,名字隻是個辨別,我們重點關注其概念:
概念:
VO(View Object):視圖對象,用于展示層,它的作用是把某個指定頁面(或元件)的所有資料封裝起來。
DTO(Data Transfer Object):資料傳輸對象,這個概念來源于J2EE的設計模式,原來的目的是為了EJB的分布式應用提供粗粒度的資料實體,以減少分布式調用的次數,進而提高分布式調用的性能和降低網絡負載,但在這裡,我泛指用于展示層與服務層之間的資料傳輸對象。
DO(Domain Object):領域對象,就是從現實世界中抽象出來的有形或無形的業務實體。
PO(Persistent Object):持久化對象,它跟持久層(通常是關系型資料庫)的資料結構形成一一對應的映射關系,如果持久層是關系型資料庫,那麼,資料表中的每個字段(或若幹個)就對應PO的一個(或若幹個)屬性。
模型:
下面以一個時序圖建立簡單模型來描述上述對象在三層架構應用中的位置:
使用者送出請求(可能是填寫表單),表單的資料在展示層被比對為VO。
展示層把VO轉換為服務層對應方法所要求的DTO,傳送給服務層。
服務層首先根據DTO的資料構造(或重建)一個DO,調用DO的業務方法完成具體業務。
服務層把DO轉換為持久層對應的PO(可以使用ORM工具,也可以不用),調用持久層的持久化方法,把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中某些Field時,可以考慮在實作層面定義出VO,這個權衡完全取決于使用架構的自動轉換能力帶來的開發和維護效率提升與設計多一個VO所多做的事情帶來的開發和維護效率的下降之間的比對。
如果頁面出現一個“大視圖”,而組成這個大視圖的所有資料需要調用多個服務,傳回多個DTO來組裝(當然,這同樣可以通過服務層提供一次性傳回一個大視圖的DTO來取代,但在服務層提供一個這樣的方法是否合适,需要在設計層面進行權衡)。
DTO與DO的差別
DTO與DO的應用
從上一節的例子中,細心的讀者可能會發現問題:既然getUser方法傳回的UserInfo不應該包含password,那麼就不應該存在password這個屬性定義,但如果同時有一個createUser的方法,傳入的UserInfo需要包含使用者的password,怎麼辦?在設計層面,展示層向服務層傳遞的DTO與服務層傳回給展示層的DTO在概念上是不同的,但在實作層面,我們通常很少會這樣做(定義兩個UserInfo,甚至更多),因為這樣做并不見得很明智,我們完全可以設計一個完全相容的DTO,在服務層接收資料的時候,不該由展示層設定的屬性(如訂單的總價應該由其單價、數量、折扣等決定),無論展示層是否設定,服務層都一概忽略,而在服務層傳回資料時,不該傳回的資料(如使用者密碼),就不設定對應的屬性。
對于DO來說,還有一點需要說明:為什麼不在服務層中直接傳回DO呢?這樣可以省去DTO的編碼和轉換工作,原因如下:
兩者在本質上的差別可能導緻彼此并不一一對應,一個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的相關資料。
http://kb.cnblogs.com/page/522348/