DCI架構是什麼?

banq發表於2010-01-07
DCI是資料Data 場景Context 互動Interactions的簡稱,DCI是一種特別關注行為的模式(可以對應GoF行為模式),而MVC模式是一種結構性模式,MVC模式由於結構化,而可能忽視了行為事件。我在javascript事件匯流排一文中也談過這個問題,Javascript這種函式式functional語言能夠幫助我們更加註重行為事件。

DCI可以說是函式式functional程式設計比如Scala帶來的一個理念,The DCI Architecture: A New Vision of Object-Oriented Programming一文(以下簡稱DCI Architecture)從OO思想根源來深入解剖DCI對傳統物件導向的顛覆。

DCI可以使用Scala的traits方便實現,Java中可以使用AOP中的Mixin來實現,也是一種面向組合程式設計,這點DDD領域驅動框架Qi4j做得比較好。忘記Scala,Qi4J是下一個 Java?

DCI Architecture認為傳統MVC只是表達了使用者介面互動中的結構,而沒有表達互動行為:

DCI架構是什麼?

它以字處理器中拼音檢查為例,拼音檢查這個行為功能放在哪裡?是dictionary 還是一個全域性的拼音檢查器呢?無論放在哪個物件內部,都顯得和這個物件內聚性不高,由此帶來多個呼叫拼音檢查行為物件之間的協作耦合,在DDD中,好像認為這種情況是使用Service服務來實現;在SOA看來,拼音檢查屬於一種規則,可由規則引擎實現,服務整合流程和規則。

DCI架構則不同於DDD這種有些折扣的處理方法,而是思路復位,重新考慮架構,從物件的資料object Data, 物件之間的協作the Collaborations between objects, 和表達需求用例中操作者角色之間的互動這三個出發點來考慮。個人感覺又把橋模式演習了一遍,其實Qi4j代表的Composer組合模式或Mixin不就是在執行時,把物件以前沒有的行為給注射進入,達到根據執行需求搭橋組合的目的。

DCI Architecture也總結了演算法和物件的關係,這點在Jdon也曾經熱烈討論過,按照OO思想,應該把演算法切分塞進物件中,Eric在DDD一書中也闡述過,不要因為大量演算法實現(屬於“做什麼”),而忽視了“是什麼”,我也在函數語言程式設計functional programming的特點 中進行了複述。

當然,演算法派還是相當不甘心的,這次總算憑藉Scala等函式式語言進行了一次“反撲”,哈哈,DCI Architecture從互動行為入手,提出瞭如果演算法橫跨多個物件,不能被切割怎麼辦呢?這個問題表面上好像提得很好,那麼過去我們是怎麼解決呢?在SOA中,這種演算法被表達為流程 工作流或規則,透過服務來進行聚合(也是一種Composer),所以,是不是可以認為DCI架構是SOA架構的另外一個翻版?

DCI Architecture認為:資料模型data model, 角色模型role model, 協作互動模型collaboration model(演算法屬於 協作互動模型)應該是程式語言核心關心點,應該在語言層次關注這三個方面。大概這是和SOA區別所在,傳統觀點:語言一般低於架構,當然,語言和架構遵循水漲船高準則。

DCI Architecture是怎麼認為資料模型呢?它認為模型應該是啞的,也就是靜止的,所以才叫資料性物件。這個我應該不能認同,如果是這樣,資料模型實際上就是失血貧血模式了,只有setter/getter方法的資料模型。

DCI Architecture那麼認為角色模式是什麼呢?感覺其說得不是很明白,因為它用程式碼案例來表達,這種從抽象直接跳到具化的思維方式我不是很喜歡,感覺邏輯上無法前後一致,因為對具體例項的邏輯解釋有很多。

在兩個賬戶之間轉賬,DCI Architecture認為在我們一般人腦海中,轉賬這個模式是獨立於賬戶的一個模型,它應該屬於一種互動interaction模型。 由此引入了roles角色模型,正如物件表達它是什麼,而角色表達的是有關物件做的一系列行為結合。

角色模型之所以對於我們如此陌生,因為我們以前的OO思維是來自OO程式,而以前的所謂OO程式包括Java/C都缺乏對角色模型的支援。角色介入混合的互動模型其實不是新概念,過去稱為algorithms演算法(和我們通常數學演算法概念有些區別)。

當然我們可以將這些互動行為按照物件邊界劃分辦法細分到一個個物件中去,不幸的是,物件邊界本身劃分實際上意味著它已經代表一些東西,比如領域知識。目前很少有這方面的建模知識:將演算法逐步精化細分到正好匹配資料模型的粒度(然後就可以裝到資料模型中,成為其方法了)。如果演算法不能精化細分,那麼我們就把演算法整個裝到一個物件中去,這樣可能將演算法中涉及到其他物件和當前物件耦合,比如上面轉賬這個演算法,如果整合到賬戶Account模型中,因為轉賬涉及到其他賬戶和money物件,那麼就將因為行為操作帶來的耦合帶到當前賬戶物件中了;當然,如果演算法可以精化細分,那麼我們把它切分到幾個部分,封裝成幾個物件的方法,這些方法都是無法表達演算法演算法高內聚性的瑣碎小方法,可謂面目全非,實際上,我們過去就是這麼幹的。

角色提供了和使用者相關的自然的邊界,以轉賬為例子,我們實際談論的是鈔票轉移,以及源賬戶和目標賬戶的角色,演算法(用例 角色行為集合)應該是這樣:
1.賬戶擁有人選擇從一個賬戶到另外一個賬戶的鈔票轉移。
2.系統顯示有效賬戶
3.使用者選擇源賬戶
4.系統顯示存在的有效賬戶
5.賬戶擁有人選擇目標賬戶。
6.系統需要數額
7.賬戶擁有人輸入數額
8.鈔票轉移 賬戶進行中(確認金額 修改賬戶等操作)

設計者的工作就是把這個用例轉化為類似交易的演算法,如下:
1.源賬戶開始交易事務
2.源賬戶確認餘額可用
3.源賬戶減少其帳目
4.源賬戶請求目標賬戶增加其帳目
5.源賬戶請求目標賬戶更新其日誌log
6.源賬戶結束交易事務
7.源賬戶顯示給賬戶擁有人轉賬成功。

程式碼如下:

template <class ConcreteAccountType>
class TransferMoneySourceAccount: public MoneySource
{
private:
 ConcreteDerived *const self() {
    return static_cast<ConcreteDerived*>(this);
 }
 void transferTo(Currency amount) {
    // This code is reviewable and
    // meaningfully testable with stubs!
    beginTransaction();
    if (self()->availableBalance() < amount) {
      endTransaction();
      throw InsufficientFunds();
    } else {
      self()->decreaseBalance(amount);
      recipient()->increaseBalance (amount);
      self()->updateLog("Transfer Out", DateTime(),
                amount);
      recipient()->updateLog("Transfer In",
             DateTime(), amount);
    }
    gui->displayScreen(SUCCESS_DEPOSIT_SCREEN);
    endTransaction();
 }
<p class="indent">

以上幾乎涵蓋了用例的所有需求,而且易懂,能夠真正表達使用者需求心理真正想要的。這稱為methodful role

角色role體現了一種通用抽象的演算法,他們沒有血肉,並不能真正做任何事情。在某些時候這一切歸結為那些表現領域模型的物件。 資料模型表達的“是什麼 what-the-system-is”,那麼有一個bank和子物件集合account, 而演算法表達的“做什麼what-the-system-does”則是在兩個賬戶之間轉移鈔票。

到這裡,我有一個疑惑,我們倡導DSL,是希望把“是什麼”和“怎麼做”分離,這裡“做什麼”和“怎麼做”是不同含義嗎?我過去認為演算法屬於怎麼做,屬於實現部分,但DCI Architecture卻認為它屬於“做什麼”部分,看來對演算法定義不同,演算法如果是數學演算法規則公式,應該屬於“怎麼做”(使用演算法實現),如果演算法屬於使用者角色的行為,那倒是屬於“做什麼”問題,但是在DDD中,我們認為“做什麼”應該屬於“是什麼”的一部分,DCI Architecture將其分離。

為什麼分離?因為“做什麼”和具體使用者角色有關,通俗講,可以看成是人和物相互互動的結果,是一種用例場景,人和物可能有各種互動場景,這就成為Context,是 Use Case scenario的Context。

看來,DCI Architecture是將“是什麼”和“做什麼”進行分離,然後根據需求在不同場景動態結合,還是橋模式的味道。

待續...



[該貼被banq於2010-01-08 11:15修改過]

相關文章