為了更好的理解 Entity與VO,我們需要先區分兩個概念: 狀態 、 標識
購物中的訂單狀態,相比大家都熟悉哈 , 比如 發貨中、 物流派送中、 商品已送達等等 。 一般訂單狀態都是使用一個欄位來表示的,比如status, status不同的值代表不同的狀態。
但是這個status就是「訂單狀態」嗎?難不成狀態就是一個欄位嗎?
我們聯想一下生活中的例子
以 今天「狀態」不錯 為例,如果狀態就是一個欄位, status=1 狀態好 status=0 狀態不好 。感覺不是很合理。
我們會發現,「狀態」實際上表示的是「目標物件在當前時刻所呈現出的內容」!在系統設計中中通過一個欄位來表示狀態只是一種簡化手段!
「狀態」表示的是「當前時刻所呈現出的內容」, 那麼說明了「狀態」是個快照/瞬態!也就是說,「目標物件」有多個「狀態」,「當前狀態」只是「目標物件」眾多「狀態」中的一個!
理解了什麼是「狀態」以後,我們就可以來初步區分Entity和VO了:
Entity在整個生命週期中,有多個「狀態」,也就是說「狀態」是可變的(至於變不變就看實際情況了)
而VO在整個生命週期中,只有一個「狀態」,也就是說「狀態」不變
對於VO來說,因為「狀態」是不可變的,我們就可以用其「狀態」來表示VO!但是對於Entity來說,因為有多個「狀態」,且「狀態」是可變的,那我們如何來表示呢?
舉個例子:假設同一個買家在同一個賣家那裡買了兩個同樣的商品,那兩個訂單裡的資訊都是一樣的,但是它是兩個不同的訂單,我們如何區分這兩個訂單呢?
那就不得不提到 「標識 」了
說到「標識」,最先想到的一般是程式語言中的「參照」或「指標」
Order orderA = new Order("productA",...);
Order orderB = new Order("productA",...);
orderA.setProductName("productB");
orderA和orderB雖然訂單資訊(狀態)都相同,但是這是兩個不同的訂單 ,. 即使改了orderA的產品名稱(狀態),依然還是訂單A。
看似解決了「區分相同狀態的不同Entity」的問題,但是沒有解決Entity有多個狀態的問題。因為「標識」指向的是目標物件的當前狀態。
語言中的這種「標識」就是無法跨系統。比如,在分散式系統中,需要保證兩個系統中的物件是同一個物件,這種「隱式標識」是做不到的。
所以「隱式標識」並不能滿足我們的需求。我們需要「顯示標識」,「顯示標識」在現實中很常見:
比如 Order
public Class Order{
orderNo // 顯示標識
product
status
...
}
設定訂單號以後,無論訂單的狀態如何變化,只要訂單號不變,那麼它就是同一個訂單。
所以,「標識」是另一個區分Entity和VO的關鍵點:
注意標識並不一定只是一個欄位,可能是多個欄位的組合,這需要根據不同的業務邏輯來確定。
Entity是具有多個「狀態」的物件,「狀態」在其生命週期中可能會改變,通過「標識」來唯一確定這個物件
VO只有一個「狀態」,且是在建立時就確定的,也就是說VO是不可變的
那麼我們如何在系統中識別哪些物件是Entity,哪些物件又是VO呢?
一個物件是表示成Entity還是VO,取決於系統的關注點
舉個例子:
「商品」在「訂單系統」中是個VO,而在「商品管理系統」中是Entity
在「商品管理系統」中,系統需要關注「商品」的「狀態」,需要維護是否上架、庫存多少、各種屬性等資訊(多種狀態)。就是說在「商品管理系統」中,商品狀態是可變的。所以它也有「標識」,即商品ID
「訂單系統」並不關心「商品」的「狀態」變化,它只關注在建立訂單時,這個「商品」的當前「狀態」是什麼,並且在訂單建立完成後,這個「商品」的「狀態」就不會再改變了
在「商品管理系統」中,商品可以這樣表示:
public class Product {
id // 商品標識
name
desc
status
...
}
而在「訂單系統」中,訂單是個Entity,商品是個VO,可以這麼表示:
public class Order{
orderNo // 訂單標識
product:Product
status
...
}
public class Product {
id // 這裡不是標識,只是狀態
name
desc
status
...
}
注意這裡的id並不是標識,這裡的id實際上退化成了狀態的一部分,保留這個id是為了和「商品管理系統」進行互動,通過id從商品管理系統中查詢商品。當然還有其它方式,例如儲存「商品管理系統」中該商品的歷史URL。