我在《小議領(lǐng)域模型Domain Model》討論了service和object,以及關(guān)聯(lián)邏輯(咳咳,該文中的一些觀點(diǎn)我已修正,合適的時(shí)候放上來(lái))。關(guān)聯(lián)邏輯就是本文討論的重點(diǎn)--specification
 
一、從DDD(Domain Driven Design)說(shuō)起 
   這個(gè)Specification來(lái)自DDD,DDD有對(duì)specification分為三個(gè)類型 
   1. Validation 
   2. Selection 
   3. Building 
   不過(guò)在初看這個(gè)Specification的概念的時(shí)候,相信很多人都有一種似曾相識(shí)的感覺(jué)。我們?nèi)粘i_發(fā)中, 基于SRC原則寫的那些xxxxProvider,xxxHelper,xxxChecker,而此外還有討論了無(wú)數(shù)遍的Dao。不就是這個(gè)specification嘛,換了一個(gè)馬甲嘛。
 
二、應(yīng)用Specification的好處: 
   現(xiàn)在就是我要高舉specification的理由了。 
   1. 統(tǒng)一Domain model的相關(guān)支持邏輯,屏蔽了dao的存在。 
   請(qǐng)注意這個(gè)selection,即從repository中(通常是數(shù)據(jù)庫(kù))取出合適的對(duì)象列表。嗯,這個(gè)有意思了,原來(lái)總覺(jué)的dao在系統(tǒng)中的地位挺尷尬的, 
   由于先前的開發(fā)理念,Domain Model沒(méi)有出現(xiàn)之前,我們是Transaction Script的開發(fā)模式,在ORM技術(shù)出現(xiàn)后,從TS轉(zhuǎn)向Domain過(guò)程中,數(shù)據(jù)庫(kù)訪問(wèn)轉(zhuǎn)成dao層, 大量的約束邏輯則散落在Service層中。因而開發(fā)系統(tǒng),無(wú)論是那張系統(tǒng)結(jié)構(gòu)(架構(gòu))圖中(可以參考AppFuse),我們見貫了Dao層的存在,甚至我們習(xí)慣了Dao的存在。 
  
   而應(yīng)用Specification則統(tǒng)一了這些概念,屏蔽了dao。dao做的最多的工作是查詢邏輯,而查詢邏輯也是一種邏輯嘛,和validation以及building都是支持Domain Model。這個(gè)specification算是給dao正名了。不過(guò)概念上正了名,如果僅僅在于此,恐怕大多數(shù)人都會(huì)說(shuō)“嗯,不錯(cuò),但是還是那么不實(shí)用”:要是按specification的做法,也就是把dao的下的代碼分散出去,沒(méi)有獲得太多的好處,搞不好還給背上類型膨脹的罪名。別急DDD還有后手呢。 
  
   2. Specification引入了FP的開發(fā)思路 
   DDD可不是技窮于此,隨后就提出了specification的closure。明眼人一看就知道這個(gè)closure來(lái)自FP。

回顧一下函數(shù)式編程基本特性先:
1. 閉包計(jì)算和高階函數(shù)。
函數(shù)本身是first class對(duì)象,閉包是起函數(shù)作用并可以像對(duì)象一樣操作的。高階函數(shù)是可以接受一個(gè)函數(shù)為參數(shù),并可以返回一個(gè)函數(shù)。
2. 延遲計(jì)算(lazy evaluation)
不是在函數(shù)綁定時(shí)計(jì)算,而是在求值時(shí)計(jì)算。
3. 遞歸的計(jì)算機(jī)制
4. 引用透明
同樣的輸入返回同樣的結(jié)果,與上下文無(wú)關(guān)。
5. 沒(méi)有副作用
賦值后不能更改,即是constant

 
   現(xiàn)在看看DDD三種specification的組合情況。 
   1. Validation的組合,DDD也給出了例子代碼--看著就清爽! 
   2. selection的組合,DDD并沒(méi)有給出例子。也不幸就是這個(gè)selection比較難辦,因?yàn)樯婕暗絪ql問(wèn)題(這個(gè)問(wèn)題往往又和性能連在一起)。剛好javaeye上有人開貼討論組合sql的問(wèn)題(http://forum.javaeye.com/viewtopic.php?t=21760) 
   我的想法是:從domain object的角度把selection的組合范圍限制在where和order兩個(gè)clause。
   這是最簡(jiǎn)單的,不考慮select和from,更不考慮join了。對(duì)于同一domain object其where和order的組合是簡(jiǎn)單的。DDD一書中的and,or和not操作是容易支持的。 
   接著要考慮支持join和union了。我估計(jì)應(yīng)用系統(tǒng)中:普通的sql(不帶join和union),帶join和union的sql以及更復(fù)雜的sql語(yǔ)句的比例是4:4:2。 
   不過(guò)join處理實(shí)在麻煩,我需要一些額外的信息。所幸從試驗(yàn)的代碼看起來(lái)還是可以接受的。 
   那么對(duì)于那些確實(shí)復(fù)雜的sql查詢?cè)趺崔k(那剩下的2)?簡(jiǎn)單。單獨(dú)寫一個(gè),我的觀點(diǎn)是太復(fù)雜的東東復(fù)用的可能性也不大,日后出現(xiàn)類似的東東可以考慮用OO的繼承的方式搞定,CO和OO本來(lái)就不是相互排斥的。
   除此外,還有和domain object無(wú)關(guān)的分頁(yè)以及”top n”就可以獨(dú)立提供specification來(lái)。
   (BTW:ajoo說(shuō)不能光說(shuō)不練,準(zhǔn)備和buuawhl在sql的parser上作文章了。向偶像學(xué)習(xí),抓緊試試看能不能作出來(lái)。)
  
   3. building的組合 
   這個(gè)看起來(lái)像curry,沒(méi)有想好,暫時(shí)不討論。 
  
   除此之外,系統(tǒng)中其它很多邏輯可以用FP的思想做,如業(yè)務(wù)規(guī)則(業(yè)務(wù)規(guī)則分類看《DSL:基于規(guī)則系統(tǒng)組織業(yè)務(wù)規(guī)則》)中的Computation,至于業(yè)務(wù)規(guī)則的Constraint和Action enabler可以直接參考validation。
BTW:ajoo在javaeye的blog給出了一些很好的例子。
 
三、其它問(wèn)題 
   到目前為止,一些看起來(lái)都還不錯(cuò)。但是還有幾個(gè)問(wèn)題沒(méi)有解決: 
   1. Selection的組合問(wèn)題是否真的可以實(shí)現(xiàn)。 
   這個(gè)。。。嘗試中 
   2. 難道在開發(fā)中要為每一種類型的specification單獨(dú)寫組合操作的處理函數(shù)。難道不能有一種統(tǒng)一的。 
   到目前為止,還不能。 對(duì)于各個(gè)specification,無(wú)論是參數(shù)還是返回對(duì)象的類型,以及組合的細(xì)節(jié)都不同,我們不得不獨(dú)立處理。 
   2.1 首先是參數(shù)還是返回對(duì)象的類型,真的統(tǒng)一的話,就會(huì)變成這樣:

interface Action{
  Object execute(Context ctxt);
}
interface Context{
  Object get(String key);
}

   很明顯這不是我們要的 
   2.2 另外就是組合細(xì)節(jié)問(wèn)題: 
   buuawhl在http://forum.javaeye.com/viewtopic.php?t=21533中列舉了3個(gè)不同類型都是我們要考慮的 
   3. 其它 
   還沒(méi)有想到,想到再說(shuō)。