物件的責任與職責

banq發表於2010-02-03
物件和資料的主要差別就是物件有行為,行為可以看成責任職責(responsibilities以下簡稱職責)的一種,理解職責是實現好的OO設計的關鍵。“Understanding responsibilities is key to good object-oriented design”—Martin Fowler 。

物件設計:角色、責任和協作"(Object Design: Roles, Responsibilities, and Collaborations)一書對物件的職責進行了完整闡述。

物件過去一直被看成是被操作的資料,這也是失血貧血模式的來由,這還是一種將物件看成資料結構或集合的另外一種表現,物件是有自主行為的,物件是一種類似機器人的概念。

職責的革命影響力
DDD領域驅動設計中,通常很多人會誤將現實中實體完全對映到軟體中領域實體,這是一種謬誤,現實中實體不是一對一反映到軟體中,這其中包括一個抽象過程。我們必須明白:軟體領域中一個實體物件通常能夠扮演多個現實中實體角色。

由於軟體領域中物件根據不同場景可以扮演不同角色,物件的方法可以看成這些角色不同職責的表現,打個比喻:你在家是一個父親,在單位是一個領導,如果我們建立兩個實體父親和領導就不合適了,沒有經過抽象分析,其實只有一個領域模型實體,就是“你”,只不過在不同場景,你扮演不同角色,角色不同反映在角色的權利或職責行為不同,父親的職責責任與領導的職責責任是不一樣的。

同一個領域物件透過不同方法扮演不同角色就帶來實現上的一個問題,這就衍生出一種DCI架構,我們建模時,不可能將其扮演的所有角色行為都塞入實體物件中,而是應該根據不同執行場景來動態分配職責,所以,傳統OO語言如Java .NET等有些難點,當然可以透過AOP的MIXIN方式變相達到,但是不是很優雅,Qi4J所謂面向組合概念實際就是這樣,而Jdonframework則從EDA事件驅動方面婉轉來透過事件訊息來驅動不同職責響應,最新面向函式式語言如Scala這方面就優雅直接了。

職責概念給OO世界帶來巨大革命性變化,使得我們分析需求必須從職責驅動重新看待需求了,DDD一書中分析貨運這個案例時,也未能從職責來審視,比如它為運輸歷史專門建立一個記錄物件,如果從職責概念看,運輸物件應該知道它自己過去的歷史,這是它的職責,所以,運輸歷史獲得應該是運輸實體物件的一個方法,而不應該為其單獨建立物件。

DDD一書很多方面還是存在資料提取的影子,這是它的歷史侷限造成的,為了擺脫失血模式純粹資料物件的影子,物件的職責上升到前所未有的高度。

如何發現職責
如果改變傳統將物件看成是死靜止的實體概念,將實體物件看成是活的,你就很容易發現其行為職責,現實世界的物件可能是做事情或代表一些資訊或東西,但是現實物件不做決定。軟體中物件是活的,根據分配給他們的職責能做決定,類似智慧機器人。

職責來自於你的軟體是如何工作。來自於軟體的HOW。尋找和分配職責需要靈感,是一個創造性活動,是一個充滿探索冒險發現新奇的樂趣活動,從下面幾個方面尋找需求中職責:
1.來自用例分析中順序圖訊息傳送。
2.構造invention、 約束表達、策略、演算法、規格Specification和描述Description都可以成為職責。
3.系統要做的事情或要管理的資訊
4.將實體物件看成一個演員(擬人化),扮演一個角色應該知道哪些事情knows something、會做那些事情do sth.,能夠控制或決定什麼事情。

簡明扼要,判斷職責的主要就是:它是否知道know這些東西,它是否會做這些東西,或做一些判斷決定等。

職責分配
將職責分配給物件,使得物件有形有態。
按照高凝聚原則分配。
使用“如果沒有這個職責,會怎樣”。
如果發現職責太廣泛,不能分配到單個物件中,那麼就切分職責,由這些小職責組合成更大職責。

所謂凝聚原則:和DDD中的高聚合概念比較類似,關注類內部;一個類是否充分實現其職責目標?類中方法是否都是為實現這個職責服務的?高聚合代表魯棒性 重用性和可理解性。

總結
一旦領域物件具有豐富的行為,變成富模型,或充血模型,它實際上就是一個Actor,Actor之間可以透過協作訊息進行聯絡,而Scala的不同於多執行緒機制的Actor模型底層實現又更好地支撐了這樣的富模型,這也是為什麼Scala開始非常流行的原因之一。

使用職責來分析需求,建立豐富物件,類似文學中的擬人化手法,將設計想象要賦予現實中靜止不會說話的客觀事物,當然,不能過,合適即可,目前最大問題是不夠。

[該貼被banq於2010-02-03 10:58修改過]

相關文章