提高軟件質量的設計 職責驅動設計 (轉載)

隨着軟件業的不斷髮展,隨着軟件需求的不斷擴大,軟件所管理的範圍也在不斷拓寬。過去一個軟件僅僅管理一臺電腦的一個小小的功能,而現在被擴展到了一個企業、一個行業、一個產業鏈。過去我們開發一套軟件,只有少量的二次開發,當它使用到一定時候我們就拋棄掉重新又開發一套。現在,隨着用戶對軟件依賴程度的不斷加大,我們很難說拋棄一套軟件重新開發了,更多的是在一套軟件中持續改進,使這套軟件的生命週期持續數年以及數個版本。正是因爲軟件業面臨着如此巨大的壓力,我們的代碼質量,我們開發的軟件擁有的可變更性和持續改進的能力,成爲軟件制勝的關鍵因素,令我們不能不反思。


代碼質量評價的關鍵指標:低耦合,高內聚

耦合就是對某元素與其它元素之間的連接、感知和依賴的量度。耦合包括:

1.元素B是元素A的屬性,或者元素A引用了元素B的實例(這包括元素A調用的某個方法,其參數中包含元素B)。

2.元素A調用了元素B的方法。

3.元素A直接或間接成爲元素B的子類。

4.元素A是接口B的實現。


如果一個元素過於依賴其它元素,一旦它所依賴的元素不存在,或者發生變更,則該元素將不能再正常運行,或者不得不相應地進行變更。因此,耦合將大大影響代碼的通用性和可變更性。


內聚,更爲專業的說法叫功能內聚,是對軟件系統中元素職責相關性和集中度的度量。如果元素具有高度相關的職責,除了這些職責內的任務,沒有其它過多的工作,那麼該元素就具有高內聚性,反之則爲低內聚性。內聚就像一個專橫的管理者,它只做自己職責範圍內的事,而將其它與它相關的事情,分配給別人去做。


高質量的代碼要求我們的代碼保持低耦合、高內聚。但是,這個要求是如此的抽象與模糊,如何才能做到這些呢?軟件大師們告訴我們了許多方法,其中之一就是Craig Larman的職責驅動設計。


職責驅動設計(Responsibility Drive Design,RDD)是Craig Larman在他的經典著作《UML和模式應用》中提出的。要理解職責驅動設計,我們首先要理解“低表示差異”。


低表示差異

我們開發的應用軟件實際上是對現實世界的模擬,因此,軟件世界與現實世界存在着必然的聯繫。當我們在進行需求分析的時候,需求分析員實際上是從客戶那裏在瞭解現實世界事物的規則、工作的流程。如果我們在軟件分析和設計的過程中,將軟件世界與現實世界緊密地聯繫到一起,我們的軟件將更加本色地還原事物最本質的規律。這樣的設計,就稱之爲“低表示差異”。

採用“低表示差異”進行軟件設計,現實世界有什麼事物,就映射爲軟件世界的各種對象(類);現實世界的事物擁有什麼樣的職責,在軟件世界裏的對象就擁有什麼樣的職責;在現實世界中的事物,因爲它的職責而產生的行爲,在軟件世界中就反映爲對象所擁有的函數。


低表示差異,使分析設計者對軟件的分析和設計更加簡單,思路更加清晰;使代碼更加可讀,閱讀者更加易於理解;更重要的是,當需求發生變更,或者業務產生擴展時,設計者只需要遵循事物本來的面貌去思考和修改軟件,使軟件更加易於變更和擴展。



角色、職責、協作

理解了“低表示差異”,現在我們來看看我們應當如何運用職責驅動設計進行分析和設計。首先,我們通過與客戶的溝通和對業務需求的瞭解,從中提取出現實世界中的關鍵事物以及相互之間的關係。這個過程我們通常通過建立領域模型來完成。領域模型建立起來以後,通過諸如Rational Rose這樣的設計軟件的正向工程,生成了我們在軟件系統中最初始的軟件類。這些軟件類,由於每個都扮演着現實世界中的一個具體的角色,因而賦予了各自的職責。前面我已經提到,如果你的系統採用職責驅動設計的思想進行設計開發,作爲一個好的習慣,你應當在每一個軟件類的註釋首行,清楚地描述該軟件類的職責。


當我們完成了系統中軟件類的制訂,分配好了各自的職責,我們就應該開始根據軟件需求,編寫各個軟件類的功能。在前面我給大家提出了一個建議,就是不要在一個函數中編寫大段的代碼。編寫大段的代碼,通常會降低代碼的內聚度,因爲這些代碼中將包含不是該軟件類應當完成的工作。作爲一個有經驗的開發人員,在編寫一個功能時,首先應當對功能進行分解。一段稍微複雜的功能,通常都可以被分解成一個個相對獨立的步驟。步驟與步驟之間存在着交互,那就是數據的輸入輸出。通過以上的分解,每一個步驟將形成一個獨立的函數,並且使用一個可以表明這個步驟意圖的釋義函數名。接下來,我們應當考慮的,就是應當將這些函數交給誰。它們有可能交給原軟件類,也有可能交給其它軟件類,其分配的原則是什麼呢?答案是否清楚,那就是職責。每個軟件類代表現實世界的一個事物,或者說一個角色。在現實世界中這個任務應當由誰來完成,那麼在軟件世界中,這個函數就應當分配給相應的那個軟件類。


通過以上步驟的分解,一個功能就分配給了多個軟件類,相互協作地完成這個功能。這樣的分析和設計,其代碼一定是高內聚的和高可讀性的。同時,當需求發生變更的時候,設計者通過對現實世界的理解,可以非常輕鬆地找到那個需要修改的軟件類,而不會影響其它類,因而也就變得易維護、易變更和低耦合了。


說了這麼多,舉一個實例也許更能幫助理解。拿一個員工工資系統來說吧。當人力資源在發放一個月工資的時候,以及離職的員工肯定不能再發放工資了。在系統設計的期初,開發人員商量好,在員工信息中設定一個“離職標誌”字段。編寫工資發放的開發人員通過查詢,將“離職標誌”爲false的員工查詢出來,併爲他們計算和發放工資。但是,隨着這個系統的不斷使用,編寫員工管理的開發人員發現,“離職標誌”字段已經不能滿足客戶的需求,因而將“離職標誌”字段廢棄,並增加了一個“離職時間”字段來管理離職的員工。然而,編寫工資發放的開發人員並不知道這樣的變更,依然使用着“離職標誌”字段。顯然,這樣的結果就是,軟件系統開始對離職員工發放工資了。仔細分析這個問題的原因,我們不難發現,確認員工是否離職,並不是“發放工資”軟件類應當完成的工作,而應當是“員工管理”軟件類應當完成的。如果將“獲取非離職員工”的任務交給“員工管理”軟件類,而“發放工資”軟件類僅僅只是去調用,那麼離職功能由“離職標誌”字段改爲了“離職時間”字段,其實就與“發放工資”軟件類毫無關係。而作爲“員工管理”的開發人員,一旦發生這樣的變更,他當然知道去修改自己相應的“獲取非離職員工”函數,這樣就不會發生以上問題。通過這樣一個實例,也許你能夠理解“職責驅動設計”的精要與作用了吧。


職責分配與信息專家

通過以上對職責驅動設計的講述,我們不難發現,職責驅動設計的精要就是職責分配。但是,在紛繁複雜的軟件設計中,如何進行職責分配常常令我們迷惑。幸運的是,Larman大師清楚地認識到了這一點。在他的著作中,信息專家模式爲我們提供了幫助。


信息專家模式(又稱爲專家模式)告訴我們,在分析設計中,應當將職責分配給軟件系統中的這樣一個軟件類,它擁有實現這個職責所必須的信息。我們稱這個軟件類,叫“信息專家”。用更加簡短的話說,就是將職責分配給信息專家。


爲什麼我們要將職責分配給信息專家呢?我們用上面的例子來說明吧。當“發放工資”軟件類需要獲取非離職員工時,“員工管理”軟件類就是“獲取非離職員工”任務的信息專家,因爲它掌握着所有員工的信息。假設我們不將“獲取非離職員工”的任務交給“員工管理”軟件類,而是另一個軟件類X,那麼,爲了獲取員工信息,軟件類X不得不訪問“員工管理”軟件類,從而使“發放工資”與X耦合,X又與“員工管理”耦合。這樣的設計,不如直接將“獲取非離職員工”的任務交給“員工管理”軟件類,使得“發放工資”僅僅與“員工管理”耦合,從而有效地降低了系統的整體耦合度。

總之,採用“職責驅動設計”的思路,爲我們提高軟件開發質量、可讀性、可維護性,以及保持軟件的持續發展,提供了一個廣闊的空間。

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