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