1. 程式人生 > >關於各種O,DO/BO/DTO/VO/AO/PO

關於各種O,DO/BO/DTO/VO/AO/PO

阿里巴巴Java開發手冊

  • 連結:https://pan.baidu.com/s/11I9ViOrat-Bw_HA8yItXwA 密碼:x5yi

2. DO/BO/DTO/VO/AO/PO

PO(persistant object) 持久物件

在 o/r 對映的時候出現的概念,如果沒有 o/r 對映,沒有這個概念存在了。通常對應資料模型 ( 資料庫 ), 本身還有部分業務邏輯的處理。可以看成是與資料庫中的表相對映的 java 物件。最簡單的 PO 就是對應資料庫中某個表中的一條記錄,多個記錄可以用 PO 的集合。 PO 中應該不包含任何對資料庫的操作。

DO(Domain Object)領域物件

就是從現實世界中抽象出來的有形或無形的業務實體。一般和資料中的表結構對應。

TO(Transfer Object) ,資料傳輸物件

在應用程式不同 tie( 關係 ) 之間傳輸的物件

DTO(Data Transfer Object)資料傳輸物件

這個概念來源於J2EE的設計模式,原來的目的是為了EJB的分散式應用提供粗粒度的資料實體,以減少分散式呼叫的次數,從而提高分散式呼叫的效能和降低網路負載,但在這裡,我泛指用於展示層與服務層之間的資料傳輸物件。

VO(view object) 值物件

檢視物件,用於展示層,它的作用是把某個指定頁面(或元件)的所有資料封裝起來。

BO(business object) 業務物件

從業務模型的角度看 , 見 UML 元件領域模型中的領域物件。封裝業務邏輯的 java 物件 , 通過呼叫 DAO 方法 , 結合 PO,VO 進行業務操作。 business object: 業務物件 主要作用是把業務邏輯封裝為一個物件。這個物件可以包括一個或多個其它的物件。 比如一個簡歷,有教育經歷、工作經歷、社會關係等等。 我們可以把教育經歷對應一個 PO ,工作經歷對應一個 PO ,社會關係對應一個 PO 。 建立一個對應簡歷的 BO 物件處理簡歷,每個 BO 包含這些 PO 。 這樣處理業務邏輯時,我們就可以針對 BO 去處理。

POJO(plain ordinary java object) 簡單無規則 java 物件

純的傳統意義的 java 物件。就是說在一些 Object/Relation Mapping 工具中,能夠做到維護資料庫表記錄的 persisent object 完全是一個符合 Java Bean 規範的純 Java 物件,沒有增加別的屬性和方法。我的理解就是最基本的 Java Bean ,只有屬性欄位及 setter 和 getter 方法!。

DAO(data access object) 資料訪問物件

是一個 sun 的一個標準 j2ee 設計模式, 這個模式中有個介面就是 DAO ,它負持久層的操作。為業務層提供介面。此物件用於訪問資料庫。通常和 PO 結合使用, DAO 中包含了各種資料庫的操作方法。通過它的方法 , 結合 PO 對資料庫進行相關的操作。夾在業務邏輯與資料庫資源中間。配合 VO, 提供資料庫的 CRUD 操作

分層領域模型規約:

  • DO( Data Object):與資料庫表結構一一對應,通過DAO層向上傳輸資料來源物件。
  • DTO( Data Transfer Object):資料傳輸物件,Service或Manager向外傳輸的物件。
  • BO( Business Object):業務物件。 由Service層輸出的封裝業務邏輯的物件。
  • AO( Application Object):應用物件。 在Web層與Service層之間抽象的複用物件模型,極為貼近展示層,複用度不高。
  • VO( View Object):顯示層物件,通常是Web向模板渲染引擎層傳輸的物件。
  • POJO( Plain Ordinary Java Object):在本手冊中, POJO專指只有setter/getter/toString的簡單類,包括DO/DTO/BO/VO等。
  • Query:資料查詢物件,各層接收上層的查詢請求。 注意超過2個引數的查詢封裝,禁止使用Map類來傳輸。

領域模型命名規約:

  • 資料物件:xxxDO,xxx即為資料表名。
  • 資料傳輸物件:xxxDTO,xxx為業務領域相關的名稱。
  • 展示物件:xxxVO,xxx一般為網頁名稱。
  • POJO是DO/DTO/BO/VO的統稱,禁止命名成xxxPOJO。

 

=====================

看了這麼多,我們可能還是很懵,這麼多內容,要記住是不可能的。必須要理解,多花些時間,完全理解也是可以的,但是上面的內容比較繞腦。

總結一下:

DO 就是資料庫的表。這個很簡單。

PO是 ORM框架特有的,比如hibernate中的User類,User類可能有id、username、password等屬性,另外User通過deptId關聯部門表,在hibernate中,其表現就是一個 Dept 引用,然後通過hbm 配置一下,這樣就關聯了起來,另外User 可能還擁有數個聯絡方式,所以我們通過一個 List 關聯 Contact 類。User類本可以完全的對映到 一個數據庫的表,但現在 User類 並沒有直接 和 User表 一致, 看起來還是有差別的。 所以這就是 PO。 如果是Mybatis呢? Mybatis雖然也是 ORM框架,但它明顯不同於hibernate,一般可以認為Mybatis的PO 就是 DO。

DTO 泛指用於展示層與服務層之間的資料傳輸物件,也就是 Controller層與 Service 層 傳輸用的, 也就是Service 層 介面的引數的型別。雖然可以理解,但是容易和VO 搞混,常常運用不好,DTO 的單向的? 還是相互的?

VO 檢視物件,用於展示層。 是前端(瀏覽器端)傳給Controller層 的資料的型別, 也就是Controller層 介面的引數的型別。

BO 難以理解,理解了也不好運用。“封裝業務邏輯的 java 物件 , 通過呼叫 DAO 方法 , 結合 PO,VO 進行業務操作”, 是不是可以認為 就是Service類? 不是的,但可以認為是Service類中用到的 封裝了PO、VO 的類。—— 這樣說,還是懵逼。 實際上,我也沒怎麼見過。搞這麼些糾纏不清的難概念,是要生生的把程式設計師玩殘玩呆逼玩白髮玩禿頂嗎。

DAO 大家用得很多,不解釋了。

TO、AO、POJO,一般不會使用。

Controller層 返回給前端的資料型別呢? 可以是VO、DTO

 

 ======================================

  • 使用者發出請求(可能是填寫表單),表單的資料在展示層被匹配為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是對現實世界各種業務角色的抽象,這就引出了兩者在資料上的區別,例如UserInfo和User(對於DTO和DO的命名規則,請參見筆者前面的一篇博文),對於一個getUser方法來說,本質上它永遠不應該返回使用者的密碼,因此UserInfo至少比User少一個password的資料。而在領域驅動設計中,正如第一篇系列文章所說,DO不是簡單的POJO,它具有領域業務邏輯。

  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的相關資料。

  到目前為止,相信大家都已經比較清晰的瞭解VO、DTO、DO、PO的概念、區別和實際應用了。通過上面的詳細分析,我們還可以總結出一個原則:分析設計層面和實現層面完全是兩個獨立的層面,即使實現層面通過某種技術手段可以把兩個完全獨立的概念合二為一,在分析設計層面,我們仍然(至少在頭腦中)需要把概念上獨立的東西清晰的區分開來,這個原則對於做好分析設計非常重要(工具越先進,往往會讓我們越麻木)。第一篇系列博文拋磚引玉,大唱領域驅動設計的優勢,但其實領域驅動設計在現實環境中還是有種種的限制,需要選擇性的使用,正如我在《田七的智慧》博文中提到,我們不能永遠的理想化的去選擇所謂“最好的設計”,在必要的情況下,我們還是要敢於放棄,因為最合適的設計才是最好的設計。本來,系列中的第二篇博文應該是討論領取驅動設計的限制和如何選擇性的使用,但請原諒我的疏忽,下一篇系列博文會把這個主題補上,敬請關注。

 

=======================================

bo dto vo po有必要劃分這麼清楚嗎

我們做各種分層,然後呢,為每一層與其他層的有向傳輸的資料 分別命名,這樣,我們就產生了很多的XxxO,

前端到Controller的可以命名一個VO,

Controller到Service的可以命名一個DTO,

Service到DAO的可以命名一個PO,

DAO返回給Service的可以命名一個DO,

Service到Controller的可以命名一個BO,

Service到前端的可以命名一個XxO,

理論上這樣當然是可以的,但是,如果 VO、DTO、PO、DO、BO、XxO 的欄位基本是一樣的, 這樣做太死板了~!

 

不可缺少的顯然是DO,有時候,我們可能會看到一些不規範的命名,比如model層,XxxModel,XxxEntity, 其實就是DO。

我之前看到有的專案,全部的傳輸引數型別基本都是 DO,根本不需要其他的O,省去了 O們之間相互轉化的麻煩,大大提高了 程式碼的可讀性。

雖然如此,我們不能一刀切,簡單的專案,全部都共用一個DO,也問題不大,但是如果大專案,需要考慮 良好的設計, 那麼增加一個VO、DTO 也不是不可以的。轉化起來雖然有些麻煩,但是各O的含義是明確的。

 

所以呢? 還是按情況來。我就是一直非常反感那種動不動一個XxO的人,看到那些醜陋的 O之間轉換的程式碼,嚴重影響了可讀性和美觀。我建議不要太多O,一般使用DTO、DO 就可以了。DTO定義在Controller層,DO定義在DAO層

 

大部分轉載至:

https://www.cnblogs.com/tsiangleo/p/4550355.html

http://www.blogjava.net/johnnylzb/archive/2010/05/27/321968.html