對於實體Entity和值對象Value Object是領域驅動設計里面兩個重要的模型對象。所以有必要對兩者的關系和區別進行理解。以下部分內容直接引用自《領域驅動設計》一書相關內容。
首先對於實體Entity,實體核心是用唯一的標識符來定義,而不是通過屬性來定義。即即使屬性完全相同也可能是兩個不同的對象。同時實體本身有狀態的,實體又演進的生命周期,實體本身會體現出相關的業務行為,業務行為會實體屬性或狀態造成影響和改變。
真正的現實世界,每個事物都一定會有唯一的標識,關鍵點是我們實際的業務場景和需求是否需要管理到唯一標識。書里面舉了一個例子,當我們發放的門票上有座位號的時候,座位需要作為獨立的實體,座位號是唯一的標識。而當先到先座模式下,我們只關心剩余座位數,那么座位號並不是唯一標識。這跟我們的業務需求有關。
一個對象不由屬性來定義,那么看人這個對象,身份證號是屬性,其實也是對於人的唯一標識。不考慮本身身份證號的位數升級,一個身份證號會跟隨你一輩子。但是對於人我們一般仍然會作為實體Entity來看待,因為人有狀態,有對象演進的生命周期,會主動產生各種行為。
對於企業內信息系統,很多時候我們把員工工卡號作為唯一標識來使用,但是要意識到工卡號只是人員的一個屬性。雖然工卡號本身不會出現兩個重復的,但是該屬性仍然可能演變,如果將工卡號作為唯一標識和ID,那么在該屬性變化時候所有其余關聯對象都將受到影響。從這個層面來看,一個唯一的內碼ID才是可信的唯一標識。
而對於值對象Value Object,它用於描述領域的某個方面本身沒有概念標識的對象,值對象被實例化后只是提供值或叫設計元素,我們只關心這些設計元素是什么?而不關心這些設計元素是誰。書里面談到顏色,數字是常見的值對象。這種對象無狀態,本身不產生行為,不存在生命周期演進。
是否為值對象跟實際的業務場景仍然關系密切。書里面又舉了地址的例子,當地址是值對象的時候,地址本身無狀態,可以被多個實際有狀態的實體使用,地址不存在太多的生命周期演進場景下地址為值對象。而對於本身行政區域管理軟件中,地址本身存在狀態,存在根據行政區域規划變化而演進的過程,因此地址為實體。
如果從值對象本身無狀態,不可變,並且不分配具體的標識層面來看。那么值對象可以僅僅理解為實際的Entity對象的一個屬性結合而已。該值對象附屬在一個實際的實體對象上面。值對象本身不存在一個獨立的生命周期,也一般不會產生獨立的行為。
值對象往往可能是多個屬性的聚合,本身無唯一標識,多個屬性最終形成的一個結果值,而這個結果值往往又依附在一個實際的實體Entity上面。那么如果從這個概念來說,值對象往往不會單獨進行持久化,或形成數據庫設計的一張數據表。另外一種情況,對於簡單的數據字典類對象,是否考慮作為值對象,這種對象需要持久化,如納稅屬性,物料類型,它們設計到數據字典中取值,這個數據字典無狀態,無自己的生命周期,是可以作為值對象來處理的。轉自:http://blog.sina.com.cn/s/blog_493a84550101534t.html
實體:在時間上有連續性,並且有唯一標識可以來區分的對象。
值對象:用來描述事物的,不區分誰是誰的,不可變的對象。
判斷一個對象是實體還是值對象,還要根據它在具體的業務領域中的實際意義來決定,比如:
體育館里的座位,當業務領域這樣規定,一張門票對應一個特定的座位,即每個座位都應該嚴格區分誰是誰,觀眾在選擇座位時根據門票對應的座位號來選擇這個唯一的座位,此時座位對象應該為實體。
但當業務領域改變規則,決定只要有門票,就可以進去隨便坐,此時不需要明確哪個座位是哪個座位,只要有座位就可以坐下,每個座位都是同一個座位對象的副本(在某些場合可以通過共享一個對象來提高性能),無須區分誰是誰,此時座位對象應該為值對象
java開發中的值對象:
在開發java的過程中經常要用到所謂的貧血模型,即只有訪問器和修改器的類(set和get方法)。今天簡單的做一下思考和分析。
在面向對象開發的初期,每個對象都是由屬性和動作兩個部分組成,后來隨着業務邏輯復雜度的升級,逐漸出現了分層,以便降低程序的邏輯和耦合性。耦合度降低的同時,原有的對象模型也被拆分,一部分是現在的值對象,也就是沒有動作的貧血模型,一部分是只有動作的驅動器,於是可以將程序簡單的划分為值對象和邏輯驅動兩個部分,然后在邏輯驅動的基礎上面再產生相應的分層。於是就有了目前所見的兩層和三層的划分模式。
今天不多說層次的划分,主要將精力放在貧血模型也就是值對象上面:
值對象,簡單的來說按照業務相關性可以划分為三類:
bo(bussiness object) -- 業務對象,業務相關性最強,每個對象之間的邏輯關系體現為業務的復雜性和邏輯性
vo (value object)-- 數據對象,主要用於服務層,對bo進行拆分,使其適用代碼邏輯的東西
po(persistent object)-- 持久化對象,和數據庫表對應的對象,用於ORM。
到此為止我想可以簡單的解釋為什么會在一個程序里面不停的出現各種不同的貧血模型了,因為意義不一樣,當然如果一個業務邏輯是簡單的,那么bo到po的重用是完全可以接受的。
通過上面的描述,我想三類定義的重點應該已經很明確了。
在設計po的時候,主要應該側重於數據庫字段和java類中的對應關系,使二者越好理解越好,比如java類中的屬性可以用userName, 對應的數據庫字段可以叫做user_name或者直接叫做username.
在設計bo的時候主要應該側重於業務邏輯的關系。就是說在設計的時候直接放棄掉數據庫的考量。一切以業務邏輯為主,比如某個表單,可能需要采集某個人的很多信息,然而在數據庫中這些信息是需要分為常用信息和非常用信息來進行存儲以便增加數據庫的performance,所以可以在設計bo的時候將相關信息設計在一個類里面,但是在傳遞給vo的時候進行分離。
vo的設計相對要比較靈活一點,具體是側重於po還是側重於bo進行設計需要自己進行考慮,我的觀點是在設計vo的時候考慮數據庫多一點,因為這一層畢竟是解耦的
