當架構模型進行迭代的過程中必然伴隨著對模型進行修改和改進我們如何防止對模型的修改又如何保證對模型進行正確的改進?
Context
架構模型通過精化合並等活動之後將會直接用於指導代碼而這個時候往往就會暴露出一些問題出來通常在實際編碼中發現架構存在或大或小的問題和錯誤導致編碼活動無法繼續這時候我們就需要對架構模型進行修改了而架構設計的過程本身是一個迭代的過程這就意味著在每一次的迭代周期中都需要對架構進行改進
Problem
我們如何避免對架構模型進行修改?又如何保證架構進行正確的改進?
Solution
我們從XP中借用了一個詞來形容架構模型的修改過程――Refactoring中文可以譯作重構這個詞原本是形容對代碼進行修改的它指的是在不改變代碼外部行為(可觀察行為)的情況下對代碼進行修改我們把這個詞用在架構模型上因為經過精化和合並之後的架構模型往往由很多個粗粒度組件構成這些組件之間存在一定的耦合度(雖然我們可以令耦合度盡可能的低但是耦合度一定是存在的)任何一個組件的重構行為都會使變化擴散到系統中的其它組件這取決於被重構的組件和其它組件之間的相對關系如果被重構的組件屬於層次較低的工具層上那麼這次的修改就可以引起模型很大的變動
在精化和合並模式中我們提到了改變和改進的區別因此我們的對策主要分為兩種如何防止改變的發生以及使用重構來改進軟件架構
防止改變的發生
在任何時候需求的變更總是對架構及軟件有著最大的傷害而需求變更中最大問題是需求蔓延很多人都有這樣的感覺項目完成之後發現初期的計劃顯得那麼陌生在項目早期對需求進行控制是重要的但並不是該模式談論的重點我們更關注在項目中期的需求蔓延問題和晚期的需求控制問題關於這方面的詳細討論請參見穩定化模式在項目中期尤其是編碼工作已經開始之後要盡可能避免出現需求蔓延的情況需求蔓延是經常發生的可能是因為用戶希望加入額外的功能或是隨著用戶對軟件了解的加深發現原有的需求存在一定的不足完全防止需求蔓延是無法做到的但是需要對其進行必要的控制例如有效的估計變更對開發測試文檔管理組織等各個方面帶來的影響
避免發生改變的另一個有效的辦法是從軟件過程著手迭代法或漸進交付法都是可用的方法一個軟件的架構設計往往是相對復雜的其中涉及到整體結構具體技術等問題一次性考慮全部的要素就很容易發生考慮不周詳的情況人的腦容量並沒有我們想象的那麼大將架構設計分為多個迭代周期來進展可以減少單次迭代周期中需要建模的架構數量因此可以減少錯誤的發生另一方面迭代次數的增多的直接結果是時間的延長此外還有一個潛在的問題如果由於設計師的失誤在後期的迭代中出現問題必然會導致大量的返工因為之前的模型已經實現了在得與失之間我們如何找到適當的平衡點呢?
迭代次數應該根據不同軟件組織的特點來制定對於初期的迭代周期而言它的主要任務應該是制定總原則(使用架構願景模式)定義層結構和各層的職責(使用分層模式)解決主要的技術問題上在這個過程中可以列出設計中可能會遇到的風險並根據風險發生的可能性和危害性來排定優先級指定專人按次序解決這些問題除此之外在初期參考前一個項目的經驗讓團隊進行設計(參見團隊設計模式)這些組織保證也是很重要初期的迭代過程是防止改變的最重要的活動
請注意需求中的非功能需求如果說功能需求定義了架構設計的目標的話非功能需求就對如何到達這個目標做出了限制例如對於實現一個報表有著多種的操作方法但是如果用戶希望新系統和舊系統進行有效的融合那麼實現的方式就需要好好的規劃了請從初期的迭代過程就開始注意非功能需求因為如果忽略它們在後期需要花費很大的精力來調整架構模型試想一下如果在項目晚期的壓力測試中發現現有的數據庫訪問方法無法滿足用戶基本的速度要求那對項目進行將會造成多麼大的影響
注意架構的穩定性在精化和合並模式中我們提到了一些模式能夠降低不同組件之間的耦合度並向調用者隱藏具體的實現接口和實現分離是設計模式最大的特點請善用這一點
盡可能的推延正式文檔的編寫在設計的初期修飾模型和編寫文檔通常都沒有太大的意義因為此時的模型還不穩定需要不斷的修改如果這時候開始投入精力開發文檔這就意味著後續的迭代周期中將會增加一項維護文檔一致性的工作了而這時候的文檔卻無法發揮出它真正的作用但是延遲文檔的編寫並不等於什麼都不做無論什麼時候進行設計都需要隨手記錄設計的思路這樣在需要的時候我們就能夠有充分的資料對設計進行文檔化的工作
對軟件架構進行重構
Martin Fowler的Refactoring一書為我們列舉了一系列的對代碼進行重構方法架構也是類似的
重構到模式
Joshua Kerievsky在《Refactoring to Patterns》一書中這樣描述重構和模式的關系
Patterns are a cornerstone of objectoriented design while testfirst programming and merciless refactoring are cornerstones of evolutionary design
(模式是面向對象設計的基石而測試優先編程和無情的重構則是設計演進的基石)作者在文中著重強調了保持適度設計的重要性
在作者看來模式常常扮演著過度設計的角色而在解決這個問題的同時又利用模式的優點的解決方法是避免在一開始使用模式而是在設計演進中重構到模式這種做法非常的有效因為在初始設計中使用模式的話你的注意力將會集中到如何使用模式上而不是集中在如何滿足需求上這樣就會導致不恰當的設計(過度設計或是設計不充分)因此在初始設計中除非非常有把握(之前有類似的經驗)否則我們應當把精力放在如何滿足需求上在初始模型完成後(參見精化和合並模式中的例子)我們會對架構進行重構而隨著迭代的演進需求的演進架構也需要演進這時候也需要重構行為在這些過程中如果發現某些部分的設計需要額外的靈活性來滿足需求那麼這時候就需要引入模式了
在軟件開發過程中我們更常的是遇見設計不充分的情況例如組件之間耦合度過高業務層向客戶端暴露了過多的方法等等很多的時候產生這種現象是由於不切實際的計劃而導致的開發人員不得不為了最終期限而趕工所有的時間都花費在新功能上而完成的軟件則被仍在一邊這樣產出的軟件是無法保證其質量的對於這種情況我們也需要對設計進行重構當然合理的計劃是大前提所在團隊的領導者必須向高層的管理者說明現在的這種做法只會導致未來的返工目前的高速開發是以犧牲未來的速度為代價的因為低劣的設計需要的高成本的維護這將抵消前期節省的成本如果軟件團隊需要可持續的發展那麼請避免這種殺雞取卵的行為
因此使用模式來幫助重構行為以實現恰當的設計
測試行為
重構的前提是測試優先測試優先是XP中很重要的一項實踐對於編碼來說測試優先的過程是先寫測試用例再編寫代碼來完成通過測試用例(過程細節不只如此請參看XP的相關書籍)但是對於架構設計來說測試行為是發生在設計之後的即在設計模型完成後產出相應的測試用例然後再編碼實現這時候測試用例就成為聯系架構設計和編碼活動的紐帶
另一方面在設計進行重構時相應的測試用例也由很大的可能性發生改變此時往往會發生需要改變的測試代碼超出普通代碼的情況避免這種情況一種做法是令你的設計模型的接口和實現相分離並使測試用例針對接口而不是實現在精化和合並模式中我們提到了一些模式能夠有助於穩定設計和測試用例Martin Fowler在他的Application Facade一文中提到使用Facade模式來分離不同的設計部分而測試則應當針對facade來進行其思路也是如此
考慮一個用戶轉帳的用例銀行需要先對用戶進行權限的審核在審核通過之後才允許進行轉帳(處於簡便起見圖中忽略了對象的創建過程和調用參數)
需要分別針對三個類編寫測試用例設計模型一旦發生變化測試用例也將需要重新編寫再考慮下面的一種情況
現在的設計引入了TransferFacade對象這樣我們的測試用例就可以針對TransferFacade來編寫了而轉帳的業務邏輯是相對比較穩定的使用這種測試思路的時候要注意兩點首先這並不是說其它的類就不需要測試用例了這種測試思路僅僅是把測試的重點放在外觀類上因為任何時候充分的測試都是不可能的但其它類的測試也是必要的對於外觀類來說任何一個業務方法的錯誤都會導致最終的測試失敗其次當外觀類的測試無法達到穩定測試用例的效果時就沒有必要使用外觀類了
只針對有需要的設計進行重構
任何時候請確保重構行為僅僅對那些有重構需要的設計重構需要花費時間和精力而無用的重構除了增大設計者的虛榮心之外並不能夠為軟件增加價值重構的需要來源於兩點一是需求的變更目前的設計可能無法滿足新的需求因此需要重構二是對設計進行改進以得到優秀簡潔的設計除了這兩種情況我們不應該對設計模型進行重構
使用文檔記錄重構的模式
應該承認模式為設計提供了充分的靈活性而這些設計部分往往都是模型的關鍵之處和難點所在因此需要對模式進行文檔化的工作甚至在必要的時候對這部分的設計進行培訓和指導確保你的團隊能夠正確的使用文檔來設計理解擴展模式我們在解決方案的前一個部分提到了盡可能延遲文檔的創建而在設計重構為模式的時候我們就需要進行文檔化的工作了因為模式具有靈活性能夠抵抗一定的變更風險
重構並保持模式的一致性
正如上一節所說的那樣模式並不是一個很容易理解的東西雖然它保持了設計的靈活性和穩定性對於面向對象的新手而言模式簡直就像是飛碟一樣由於缺少面向對象的設計經驗他們無法理解模式的處理思路在實踐中我們不只一次的碰到這種情況我們不得不重頭開始教授關於模式的課程因此最後我們在軟件設計采用一定數量的模式並確保在處理相同問題的時候使用相同的模式這樣應用的模式就成為解決某一類的問題的標准做法從而在一定程度上降低了學習的曲線
保持模式的一致性的另一個方面的含義是將模式作為溝通的橋梁軟件開發是一種團隊的行為因此溝通在軟件開發中扮演著很重要的角色試想一下開發人員在討論軟件設計的時候只需要說使用工廠模式大家就都能夠明白而不是費勁口舌的說明幾個類之間的關系這將大大提高溝通的效率此外模式的使用和設計的重構對於提高團隊的編程水平培養後備的設計人員等方面都是很有意義的
From:http://tw.wingwit.com/Article/program/Java/gj/201311/11156.html