實現領域驅動設計----第五章

爲什麼使用實體

唯一的身份標識和可變性特徵將實體對象和值對象區分開來。

有時,實體並不見得是一種適當的建模工具,而我們對實體的使用也有可能是不恰當的。很多時候,一個領域概念應該建模成值對象,而不是實體對象。

應用程序生成唯一標識
有很多可靠的方法都可以自動生成唯一標識,但是如果應用程序處於集羣環境或者分佈在不同的計算節點中,我們就需要額外小心了。有些方法可以生成完全唯一的標識,比如UUID(Universally Unique Identifier)或者GUID(Globally Unique Identifier)。以下是生成唯一標識的另一種方法,其中每一步生成的結果都將添加到最終的文本標識中:

  1. 計算節點的當前時間,以毫秒記
  2. 計算節點的IP地址
  3. 虛擬機(Java)中工廠對象實例的對象標識
  4. 虛擬機(Java)中由同一個隨機數生成器生成的隨機數

以下方式生成一個基於名字的UUID:

String rawId = java.util.UUID.nameUUIDFromBytes("Some text".getBytes()).toString();

另外,我們還可以對所生成的僞隨機數進行加密:

SecureRandom randomGenerator = new SecureRandom();
int randomNumber = randomGenerator.nextInt();
String randomDigits = new Integer(randomNumber).toString();
MessageDigest encryptor = MessageDigest.getInstance("SHA-1");
byte[] rawIdBytes = encryptor.digest(randomDigits.getBytes());

裏插入圖片描述

將標識生成延遲到實體持久化時還有另外一個問題。當兩個或多個實體需要加入到集合java.util.Set中時,由於此時它們都還沒有實體標識,它們的默認標識值都是相等的(比如都爲null,或者0,或者1)。如果實體的equals()方法比較的是實體標識,此時所有新建實體對象都被認爲是同一個對象。所以只有第一個對象被保留下來,所有其他對象都被排除在集合之外。這將導致一個嚴重的bug,並且是一個難以查找的bug。

層超類型(Layer Super type)

public abstract class IdentifiedDomainObject implements Serializabel{
private long id = -1;
public IdentifiedDomainObject(){
super();
}
protected long id(){
return thid.id;
}

protected void setId(long anId){
this.id = anId;
}
}

這裏的IdentifiedDomainObject便是層超類型,這是一個抽象基類,通過protected關鍵字,它向客戶端隱藏了委派主鍵。所有實體都擴展自該抽象基類。早實體所處的模塊之外,客戶端根本就不用關心id這個委派標識。我們甚至可以將protected換爲private。Hibernate既可以通過getter和setter方法來訪問屬性,也可以通過反射機制直接訪問對象屬性,故無論是使用protected還是private都是無關緊要的。另外,層超類型還有其他好處,比如支持樂觀鎖。

發現實體及其本質特徵

限界上下文中的通用語言向我們提供了設計領域模型的概念術語。通用語言不是平白產生的,它必須通過與領域專家詳細討論之後才能得到。在通過語言的術語中,名詞用於給概念命名,形容詞用於描述這些概念,而動詞則表示可以完成的操作。但是,如果我們認爲對象就是一組命名的類和類上定義的操作,除此之外並不包含其他內容,那麼,我們就錯了。在領域模型中還可以包含很多其他內容。團隊討論和規範文檔可以幫助我們創建更有意義的通用語言。

對象分裂症描述的是:委派對象根本不知道原來被委派對象的身份標識,因此我們無法知道委派對象那個的真正身份。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章