Java視線論壇 :: 閱讀主題 - 總結(jié)一下最近關(guān)于domain object以及相關(guān)的討論
關(guān)于domain object的討論
既然大家都統(tǒng)一了觀點(diǎn),那么就有了一個(gè)很好的討論問題的基礎(chǔ)了。Martin Fowler的Domain Model,或者說我們的第二種模型難道是完美無缺的嗎?當(dāng)然不是,接下來我就要分析一下它的不足,以及可能的解決辦法,而這些都來源于我個(gè)人的實(shí)踐探索。
在第二種模型中,我們可以清楚的把這4個(gè)類分為三層:
1、實(shí)體類層,即Item,帶有domain logic的domain object
2、DAO層,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和實(shí)現(xiàn)類
3、業(yè)務(wù)邏輯層,即ItemManager,接受容器事務(wù)控制,向Web層提供統(tǒng)一的服務(wù)調(diào)用
在這三層中我們大家可以看到,domain object和DAO都是非常穩(wěn)定的層,其實(shí)原因也很簡單,因?yàn)閐omain object是映射數(shù)據(jù)庫字段的,數(shù)據(jù)庫字段不會(huì)頻繁變動(dòng),所以domain object也相對穩(wěn)定,而面向數(shù)據(jù)庫持久化編程的DAO層也不過就是CRUD而已,不會(huì)有更多的花樣,所以也很穩(wěn)定。
問題就在于這個(gè)充當(dāng)business workflow facade的業(yè)務(wù)邏輯對象,它的變動(dòng)是相當(dāng)頻繁的。業(yè)務(wù)邏輯對象通常都是無狀態(tài)的、受事務(wù)控制的、Singleton類,我們可以考察一下業(yè)務(wù)邏輯對象都有哪幾類業(yè)務(wù)邏輯方法:
第一類:DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。
ItemManager之所以要代理這種類,目的有兩個(gè):向Web層提供統(tǒng)一的服務(wù)調(diào)用入口點(diǎn)和給持久化方法增加事務(wù)控制功能。這兩點(diǎn)都很容易理解,你不能既給Web層程序員提供xxxManager,也給他提供xxxDao,所以你需要用xxxManager封裝xxxDao,在這里,充當(dāng)了一個(gè)簡單代理功能;而事務(wù)控制也是持久化方法必須的,事務(wù)可能需要跨越多個(gè)DAO方法調(diào)用,所以必須放在業(yè)務(wù)邏輯層,而不能放在DAO層。
但是必須看到,對于一個(gè)典型的web應(yīng)用來說,絕大多數(shù)的業(yè)務(wù)邏輯都是簡單的CRUD邏輯,所以這種情況下,針對每個(gè)DAO方法,xxxManager都需要提供一個(gè)對應(yīng)的封裝方法,這不但是非??菰锏?,也是令人感覺非常不好的。
第二類:domain logic的方法代理。就是上面例子中placeBid方法。雖然Item已經(jīng)有了placeBid方法,但是ItemManager仍然需要封裝一下Item的placeBid,然后再提供一個(gè)簡單封裝之后的代理方法。
這和第一種情況類似,其原因也一樣,也是為了給Web層提供一個(gè)統(tǒng)一的服務(wù)調(diào)用入口點(diǎn)和給隱式的持久化動(dòng)作提供事務(wù)控制。
同樣,和第一種情況一樣,針對每個(gè)domain logic方法,xxxManager都需要提供一個(gè)對應(yīng)的封裝方法,同樣是枯燥的,令人不爽的。
第三類:需要多個(gè)domain object和DAO參與協(xié)作的business workflow。這種情況是業(yè)務(wù)邏輯對象真正應(yīng)該完成的職責(zé)。
在這個(gè)簡單的例子中,沒有涉及到這種情況,不過大家都可以想像的出來這種應(yīng)用場景,因此不必舉例說明了。
通過上面的分析可以看出,只有第三類業(yè)務(wù)邏輯方法才是業(yè)務(wù)邏輯對象真正應(yīng)該承擔(dān)的職責(zé),而前兩類業(yè)務(wù)邏輯方法都是“無奈之舉”,不得不為之的事情,不但枯燥,而且令人沮喪。
分析完了業(yè)務(wù)邏輯對象,我們再回頭看一下domain object,我們要仔細(xì)考察一下domain logic的話,會(huì)發(fā)現(xiàn)domain logic也分為兩類:
第一類:需要持久層框架隱式的實(shí)現(xiàn)透明持久化的domain logic,例如Item的placeBid方法中的這一句:
java代碼: |
this.getBids().add(newBid);
|
上面已經(jīng)著重提到,雖然這僅僅只是一個(gè)Java集合的添加新元素的操作,但是實(shí)際上通過事務(wù)的控制,會(huì)潛在的觸發(fā)兩條SQL:一條是insert一條記錄到bid表,一條是更新item表相應(yīng)的記錄。如果我們讓Item脫離Hibernate進(jìn)行單元測試,它就是一個(gè)單純的Java集合操作,如果我們把他加入到Hibernate框架中,他就會(huì)潛在的觸發(fā)兩條SQL,這就是隱式的依賴于持久化的domain logic。
特別請注意的一點(diǎn)是:在沒有Hibernate/JDO這類可以實(shí)現(xiàn)“透明的持久化”工具出現(xiàn)之前,這類domain logic是無法實(shí)現(xiàn)的。
對于這一類domain logic,業(yè)務(wù)邏輯對象必須提供相應(yīng)的封裝方法,以實(shí)現(xiàn)事務(wù)控制。
第二類:完全不依賴持久化的domain logic,例如readonly例子中的Topic,如下:
Topic
{ boolean isAllowReply
() { Calendar dueDate =
Calendar.
getInstance();
dueDate.
setTime(lastUpdatedTime
);
dueDate.
add(Calendar.
DATE, forum.
timeToLive);
Date now =
new
Date
();
return now.
after(dueDate.
getTime());
} }
注意這個(gè)isAllowReply方法,他和持久化完全不發(fā)生一丁點(diǎn)關(guān)系。在實(shí)際的開發(fā)中,我們同樣會(huì)遇到很多這種不需要持久化的業(yè)務(wù)邏輯(主要發(fā)生在日期運(yùn)算、數(shù)值運(yùn)算和枚舉運(yùn)算方面),這種domain logic不管脫離不脫離所在的框架,它的行為都是一致的。對于這種domain logic,業(yè)務(wù)邏輯層并不需要提供封裝方法,它可以適用于任何場合。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點(diǎn)擊舉報(bào)。