DCI架構是什麼?

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只是表達了用戶界面交互中的結構,而沒有表達交互行爲:


它以字處理器中拼音檢查爲例,拼音檢查這個行爲功能放在哪裏?是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();
 }


以上幾乎涵蓋了用例的所有需求,而且易懂,能夠真正表達用戶需求心理真正想要的。這稱爲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是將“是什麼”和“做什麼”進行分離,然後根據需求在不同場景動態結合,還是橋模式的味道。

上貼總算搞明白:  DCI   Architecture “是什麼”問題,哈哈,有點繞人,DCI Architecture自己也是有關“是什麼”的。

DCI Architecture一文下半部就是如何實現它的架構思想,是關於“怎麼做”的了,建議傳統語言在編譯時,就將角色的行爲或算法混合Mixin到數據模型類中,這是典型的AOP思想。

下圖就是DCI   Architecture架構把MVC模式肢解,將C和V用對應的Context來替代。


這樣,DCI架構真正含義可以歸結如下:
1.數據data:是領域對象中代表領域類概念的那部分。
2.場景context:根據運行時即時調用,將活的對象實例帶到符合用例需求的場景中
3.交互interactions, 描述需求用戶心目中角色的活動算法。

就象上圖中,把場景Context看成是一張表,角色行爲作爲橫行加入,而數據模型作爲縱行加入。

具體實現,可以在運行時,通過動態反射將業務邏輯行爲注射到領域模型對象中,動態語言比較方便,C++ 和 C#使用pre-load預加載,Scala使用hybrid 混合,DCI Architecture一文沒有提到AOP,可以使用AOP中靜態weave方式混合,現在******it等動態代理框架都支持靜態weave,包括AspectJ/Spring,在編譯時就將業務行爲注射到模型中。

DCI Architecture一文接下來詳細介紹了Scala中的traits 是如何實現這一注射的。traits 能夠讓方法在程序運行時注射到一個對象實例中:

trait TransferMoneySourceAccount extends
 SourceAccount {
  this
: Account =>

  // This code is reviewable and testable!


  def transferTo(amount: Currency) {
    beginTransaction()
    if
 (availableBalance < amount) {
        . . . .
    }
}

. . . .

//通過下面特別的對象創建方式生成符合用例的源賬戶和目標賬戶


val source = new
 SavingsAccount with TransferMoneySourceAccount
val destination = new
 CheckingAccount with TransferMoneyDestinationAccount



個人思考:在代碼編譯時混合注射已經不是新鮮方式,Spring2.0開始已經可以做到,Scala以一種更易懂代碼方式實現,現在需要思考:我們這樣做的目的是什麼?就是實現Context場景混合,說白了,就是到用戶現場燒菜。

條條大路通羅馬,爲實現這一目標,我們可以採取另外一種方式,用戶現場的本質是什麼?用戶現場爲什麼是活的,Context爲什麼是活的?因爲用戶的動作,動作引發事件,因此,事件模式可能是Context的本質。

如果是這樣,只要我們遵循事件編程模型如EDA架構,也許也能實現DCI 架構?比如通過Domain Events來激活角色行爲:

賬戶擁有人操作自己的賬戶(領域模型),這個賬戶領域模型發出事件,驅動目標賬戶進行帳目更新,最後返回給賬戶擁有人,轉賬成功。

繞了半天,什麼OO ,什麼算法,用事件模式就搞定了?

 

原文:http://www.jdon.com/jivejdon/thread/37976

發佈了215 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章