使用qi4j實現DCI架構

我曾經DCI架構是什麼? 在一文中提到Qi4j框架實現DCI 架構比較好,dzone今天就有一篇文章專門談Implementing  DCI   in Qi4j。

DCI是一種新的構建面向對象應用的方法途徑,DCI: Data數據, Context場景, Interaction交互。該文談了如何使用DCI 構建一個REST   API。

Roles
DCI核心思想是:對象不是由單個類如POJO組成的,而是使用Roles角色來組合組裝功能,再具體一點,當我們在這裏說“對象Object”,是指DDD 中的實體,而不是值對象或其他。在帶有保存數據的企業架構中,實體將被劃分爲很多Roles,每個Roles有各自的職責目標,這點可以使用Qi4j的Mixin完成(AOP),或者Scala的Traits,否則很難以實現。

Data
DCI的數據代表實體對象中的數據,數據是進行私有Mixin(Private mixin)混合的,不可以被實體對象以外對象訪問,Private mixin是一個重要概念,可以在不使用private語法的情況下,讓狀態成爲對象的私有屬性。

Context
Context掌握了在一個交互場景中角色與一個特定對象實例的關係映射。COntext可以使用POJO或Qi4j的TransientComposites 實現,後者功能很強大,體現了面向組合的架構特點。

Interactions
交互是作爲場景實現的方法,尋找場景中相關的角色,然後激活調用其領域方法,重要一點是,交互的方法應該對應於用戶界面表現層的Action方法,也就是MVC中控制器的方法。如果你改變了你的界面,就改變相應的場景和交互。

該文結合了作者項目,演示了DCI 的使用情況,這個項目只有三個簡單領域模型: Project, User, 和 Task,Task是一種可分配的角色,而Project和 User 是一種分配者的角色,User處於一種被分配的角色,這樣,我們可以有一個簡單場景叫InboxContext,有一個方法(或者叫交互):assign(Assignable):分配(可分配)。

Project有一個帶有Task的InBox,那是我們要分配給用戶的Task。但是它既可以被一個User擁有,也可以被一個特定Project擁有,這樣就需要角色職責分配,更多職責發現和切分見:對象的責任與職責

切分的目標是達成這樣:"assignment"不和這樣場景綁定:"查詢一個用戶的tasks列表, 他們其中一個能夠分配給這個用戶",而應該是: "查詢可分配者的分配Assignments列表, 選擇其中一個分配給分配者"。這兩者區別就是,與具體用戶解耦,從對象職責行爲這個新角度來考慮場景,交互和場景與具體類解耦,只和角色有關係,我們就可以在角色這個層次來思考設計。

這種方式可以讓程序員側重角色這個抽象層面來考慮,然後着重瞭解職責“分配”是如何工作的,而不必着眼於用戶,因爲用戶有用戶名和密碼 有個人簡歷等等很多細節資料;如果我們以後使用Calculation替代Task,使用ClusterNode替代User,這樣我們就不必修改這個系統,可以複用它了,唯一修改的就是對象和角色的映射,如果熟悉RBAC基於角色權限設計原理的話,應該對這種從角色和職責角度考慮方式的認同。

該文還使用代碼表達瞭如何實現交互,如下代碼:

InteractionContext map = new
 InteractionContext();
RootContext context = assembler.objectBuilderFactory().newObjectBuilder( RootContext.class
 ).use(stack ).newInstance();
InboxContext inboxContext = context.user( userId ).inbox();

爲當前場景創造了RootContext,這是所有場景的根場景,從根場景能夠創建子場景InboxContext ,其中userId用來尋找特定的User,如果使用REST 實現,這個userId就使用URL實現,比如:"/administrator/inbox" .

爲了實現前面"查詢可分配者的分配Assignments列表, 選擇其中一個分配給分配者",代碼如下:
class
 AssignmentsMixin
     implements Assignments
 {
     @This   //自動注入Data


     AssignmentsData data;

     
//選擇其中一個分配給分配者


     public
 void
 assignTo( Assignable assignable, Assignee assignee )
     {
         assignable.assignTo( assignee );
         data.assignments().add( assignable );
     }

     
//查詢可分配者的分配Assignments列表


     public
 Iterable<Assignable> assignments()
     {
         return
 data.assignments();
     }
 }


我們如果實現將任務Task分配給某個用戶,就使用如下代碼:
inboxContext.assignTo( task );


更多代碼可見原文有下載。

最後,該文總結了這樣做的優點:

以上代碼可能多了些,但是能夠完成代碼的可讀性, 代碼的可維護性, 易於改變拓展性,如果你使用普通POJO方法,把所有職責放在一個類中,好像很簡單了,一旦這個軟件項目發展到一定程度,就難以拓展維護。

此外還有優點是可以重用複用,能夠避免貧血模型,在目前所謂主流架構Spring或EJB 之中,你爲了避免將所有行爲方法放入一個大類中,將數據放在實體中,將行爲分開放到服務Service中(見請問一下這樣分層對不對 ),這實際破壞了封裝,就是MF指責的失血模型,問題擺在那裏,但是一直沒有得到解決,  DCI 架構解決了這個問題。

 

和xmuzyu討論了以Jive Jdon案例說明對象職責和SOLID原則應用 一文中的“閱讀次數”到底應不應該屬於帖子這個對象的屬性,其實這個問題存在很多案例中,“閱讀次數”可以說不是帖子的固有屬性,帖子這個對象離開“閱讀次數”這個屬性不是不能存在,探究“閱讀次數”這個屬性和固有屬性比如帖子的名稱等是有區別的,屬於一種場景屬性,也就是說只有在閱讀這個場景下才會發生的屬性。

DCI架構本質:DCI: 對象的Data數據, 對象使用的Context場景, 對象的Interaction交互行爲,我們知道,對象有數據屬性和方法行爲,以前我們是封裝在一個對象中,爲什麼要封裝在一個對象中?因爲這個對象在某個需求用例場景中被使用時需要這些屬性和方法行爲,注意了,這裏面有一個關鍵點,就是對象被使用,以前我們進行面向對象設計,是遵循一種靜態原則,因爲這個對象被使用需要這些屬性和行爲,所以,我們在編碼時將這些屬性和行爲寫在這個類中。

這個邏輯過程是不對的,那是因爲過去程序語言平臺落後,導致了我們這種思維邏輯,現在是的思維邏輯是:對象被使用時需要的屬性和行爲不必一定要在編寫代碼時寫入,而是在運行時再注入或MiXIN混合進去。

這就是DCI 架構的本質。

我們還是以“閱讀次數”這個案例分析,在以Jive Jdon案例說明對象職責和SOLID原則應用 一文我們討論焦點集中在“閱讀次數”到底應不應該屬於帖子這個對象,其實這個角度有問題了,如果按照DCI 架構和對象角色職責這個架構來考慮,應該這樣:

我們除去具體事物如用戶和帖子,而是從角色來分析這個場景,就像上貼中存在“分配者”和“被分配者”一樣,這裏存在兩個角色“閱讀者”和“被閱讀者”,而場景Context是和“閱讀者”有關的一個對象,那麼一個帖子被閱讀的建模描述如下:
第一步:根據用戶創建一個閱讀場景對象:
ReadContext readcontext = RootContext.create(userId)
第二步:由閱讀場景對象來執行交互行爲閱讀:
readcontext.view(readed);

其中readed被閱讀者就是帖子。

所以,按照DCI 架構來說,閱讀這個行爲應該屬於Context場景這個行爲,只有在這個場景下才會發生閱讀這個行爲。

如果不按照DCI 架構來分析,我們會傾向把閱讀這個行爲放到“帖子”這個對象中,有人擔心,以後再有“頂”這個行爲,那麼頂的結果數據也要放到“帖子”這個對象中,這裏有一個誤區,“帖子”不是一個類,不是說所有場景屬性結果都放如帖子這個一個類中,帖子只是一個對象羣中根實體,我們可以根據不同場景創建不同實體類或值對象,都從屬於“帖子”這個根實體,組成一個邊界和子領域。

當然,如果有Qi4j或AOP的Mixin來支持,我們也可以使用樓上這種DCI 架構方式。

但是個人認爲,上面AssignmentsMixin類實際是和業務無關的器具技術類,如果語言本身提供AssignmentsMixin這個混合機制,就不再需要了。

另外,DCI架構中對於我們普通的POJO技術,也就是沒有Mixin支持的環境中,最大的借鑑就是引入Context這個場景對象,D和I以前都有,就是對象的數據和方法,通過Context這個對象引入,使的我們的軟件更加貼近需求分析中用例場景,四色原型可以說是DCI 架構的前言。

Context這個對象其實和角色動作職責有關,如果你不是管理者,你就不可能進入管理這個場景,角色是場景的前置條件,交互動作是場景的必然結果,很符合DBC設計原則。

這應該是面向對象設計領域新的革命思維。

 

DCI架構的核心是Context,場景是角色參與具體業務活動的表現,從用例圖可以看出,如下圖:




這張圖是典型的需求用例圖,這是通往軟件的第一步,通常我們從用例圖到軟件建模,有一個轉換,那是因爲過去軟件技術不行,只能做妥協轉換,而根據DCI 架構,我們可以在軟件中直接實現用例需求。

DCI架構的場景有時很象SOA中的服務,服務確實爲對象被使用提供了場景,所以,很多場景是在提供服務時發生的。但是必須注意到,交互行爲屬於場景這個範圍內,你不能忽視場景這個邊界,而直接將交互行爲方法寫到服務中,這就造成了服務類過大,回到面向過程編程了。見這個有關Spring下的分層討論

 

原文:http://www.jdon.com/jivejdon/thread/38266#23127442

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章