當架構模型進行迭代的過程中,必然伴隨著對模型進行修改和改進。我們如何防止對模型的修改,又如何保證對模型進行正確的改進?
context
架構模型通過精化、合併等活動之後,將會直接用於指導**。而這個時候,往往就會暴露出一些問題出來,通常在實際編碼中,發現架構存在或大或小的問題和錯誤,導致編碼活動無法繼續。這時候我們就需要對架構模型進行修改了。而架構設計的過程本身是乙個迭代的過程,這就意味著在每一次的迭代週期中,都需要對架構進行改進。
problem
我們如何避免對架構模型進行修改?又如何保證架構進行正確的改進?
solution
我們從xp中借用了乙個詞來形容架構模型的修改過程――refactoring,中文可以譯作重構。這個詞原本是形容對**進行修改的。它指的是在不改變**外部行為(可觀察行為)的情況下對**進行修改。我們把這個詞用在架構模型上,因為經過精化和合併之後的架構模型往往由很多個粗粒度元件構成。這些元件之間存在一定的耦合度(雖然我們可以令耦合度盡可能的低,但是耦合度一定是存在的),任何乙個元件的重構行為都會使變化擴散到系統中的其它元件。這取決於被重構的元件和其它元件之間的相對關係。如果被重構的元件屬於層次較低的工具層上,那麼這次的修改就可以引起模型很大的變動。
在精化和合併模式中,我們提到了改變和改進的區別,因此,我們的對策主要分為兩種:如何防止改變的發生,以及,使用重構來改進軟體架構。
防止改變的發生
在任何時候,需求的變更總是對架構及軟體有著最大的傷害。而需求變更中最大問題是需求蔓延。很多人都有這樣的感覺,專案完成之後,發現初期的計畫顯得那麼陌生。在專案早期對需求進行控制是重要的,但並不是該模式談論的重點。我們更關注在專案中期的需求蔓延問題和晚期的需求控制問題。關於這方面的詳細討論,請參見穩定化模式。在專案中期,尤其是編碼工作已經開始之後,要盡可能避免出現需求蔓延的情況。需求蔓延是經常發生的,可能是因為使用者希望加入額外的功能,或是隨著使用者對軟體了解的加深,發現原有的需求存在一定的不足。完全防止需求蔓延是無法做到的,但是需要對其進行必要的控制。例如,有效的估計變更對開發、測試、文件、管理、組織等各個方面帶來的影響。
避免發生改變的另乙個有效的辦法是從軟體過程著手。迭代法或漸進交付法都是可用的方法。乙個軟體的架構設計往往是相對複雜的,其中涉及到整體結構、具體技術等問題。一次性考慮全部的要素,就很容易發生考慮不周詳的情況。人的腦容量並沒有我們想象的那麼大。將架構設計分為多個迭代週期來進展,可以減少單次迭代週期中需要建模的架構數量,因此可以減少錯誤的發生。另一方面,迭代次數的增多的直接結果是時間的延長,此外還有乙個潛在的問題,如果由於設計師的失誤,在後期的迭代**現問題,必然會導致大量的返工。因為之前的模型已經實現了。在得與失之間,我們如何找到適當的平衡點呢?
迭代次數應該根據不同軟體組織的特點來制定,對於初期的迭代週期而言,它的主要任務應該是制定總原則(使用架構願景模式)、定義層結構和各層的職責(使用分層模式)、解決主要的技術問題上。在這個過程中,可以列出設計中可能會遇到的風險,並根據風險發生的可能性和危害性來排定優先順序,指定專人按次序解決這些問題。除此之外,在初期參考前乙個專案的經驗,讓團隊進行設計(參見團隊設計模式),這些組織保證也是很重要。初期的迭代過程是防止改變的最重要的活動。
請注意需求中的非功能需求。如果說功能需求定義了架構設計的目標的話,非功能需求就對如何到達這個目標做出了限制。例如,對於實現乙個報表有著多種的操作方法,但是如果使用者希望新系統和舊系統進行有效的融合,那麼實現的方式就需要好好的規劃了。請從初期的迭代過程就開始注意非功能需求,因為如果忽略它們,在後期需要花費很大的精力來調整架構模型。試想一下,如果在專案晚期的壓力測試中,發現現有的資料庫訪問方法無法滿足使用者基本的速度要求,那對專案進行將會造成多麼大的影響。
注意架構的穩定性。在精化和合併模式中,我們提到了一些模式,能夠降低不同元件之間的耦合度。並向呼叫者隱藏具體的實現。介面和實現分離是設計模式最大的特點,請善用這一點。
盡可能的推延正式文件的編寫。在設計的初期,修飾模型和編寫文件通常都沒有太大的意義。因為此時的模型還不穩定,需要不斷的修改。如果這時候開始投入精力開發文件,這就意味著後續的迭代週期中將會增加一項維護文件一致性的工作了。而這時候的文件卻無法發揮出它真正的作用。但是,延遲文件的編寫並不等於什麼都不做,無論什麼時候進行設計,都需要隨手記錄設計的思路。這樣在需要的時候,我們就能夠有充分的資料對設計進行文件化的工作。
對軟體架構進行重構
martin fowler
的refactoring一書為我們列舉了一系列的對**進行重構方法。架構也是類似的。
重構到模式
joshua kerievsky
在《refactoring to patterns》一書中這樣描述重構和模式的關係:
patterns are a cornerstone of object-oriented design, while test-first programming and merciless refactoring are cornerstones of evolutionary design
(模式是物件導向設計的基石,而測試優先程式設計和無情的重構則是設計演進的基石)。作者在文中著重強調了保持適度設計的重要性。
在作者看來,模式常常扮演著過度設計的角色。而在解決這個問題的同時又利用模式的優點的解決方法是避免在一開始使用模式,而是在設計演進中重構到模式。這種做法非常的有效,因為在初始設計中使用模式的話,你的注意力將會集中到如何使用模式上,而不是集中在如何滿足需求上。這樣就會導致不恰當的設計(過度設計或是設計不充分)。因此,在初始設計中,除非非常有把握(之前有類似的經驗),否則我們應當把精力放在如何滿足需求上。在初始模型完成後(參見精化和合併模式中的例子),我們會對架構進行重構,而隨著迭代的演進,需求的演進,架構也需要演進,這時候也需要重構行為。在這些過程中,如果發現某些部分的設計需要額外的靈活性來滿足需求,那麼這時候就需要引入模式了。
在軟體開發過程中,我們更常的是遇見設計不充分的情況,例如元件之間耦合度過高,業務層向客戶端暴露了過多的方法等等。很多的時候,產生這種現象是由於不切實際的計畫而導致的。開發人員不得不為了最終期限而趕工,所有的時間都花費在新功能上,而完成的軟體則被仍在一邊。這樣產出的軟體是無法保證其質量的。對於這種情況,我們也需要對設計進行重構,當然,合理的計畫是大前提所在。團隊的領導者必須向高層的管理者說明,現在的這種做法只會導致未來的返工,目前的高速開發是以犧牲未來的速度為代價的。因為低劣的設計需要的高成本的維護,這將抵消前期節省的成本。如果軟體團隊需要可持續的發展,那麼請避免這種殺雞取卵的行為。
因此,使用模式來幫助重構行為,以實現恰當的設計。
測試行為
考慮乙個使用者轉帳的用例。銀行需要先對使用者進行許可權的審核,在審核通過之後才允許進行轉帳(處於簡便起見,圖中忽略了物件的建立過程和呼叫引數):
需要分別針對三個類編寫測試用例,設計模型一旦發生變化,測試用例也將需要重新編寫。再考慮下面的一種情況:
現在的設計引入了transfe***cade物件,這樣我們的測試用例就可以針對transfe***cade來編寫了,而轉帳的業務邏輯是相對比較穩定的。使用這種測試思路的時候,要注意兩點:首先,這並不是說其它的類就不需要測試用例了,這種測試思路僅僅是把測試的重點放在外觀類上,因為任何時候充分的測試都是不可能的。但其它類的測試也是必要的,對於外觀類來說,任何乙個業務方法的錯誤都會導致最終的測試失敗。其次,當外觀類的測試無法達到穩定測試用例的效果時,就沒有必要使用外觀類了。
只針對有需要的設計進行重構。
任何時候,請確保重構行為僅僅對那些有重構需要的設計。重構需要花費時間和精力,而無用的重構除了增大設計者的虛榮心之外,並不能夠為軟體增加價值。重構的需要**於兩點:一是需求的變更。目前的設計可能無法滿足新的需求,因此需要重構。二是對設計進行改進,以得到優秀簡潔的設計。除了這兩種情況,我們不應該對設計模型進行重構。
使用文件記錄重構的模式。
應該承認,模式為設計提供了充分的靈活性。而這些設計部分往往都是模型的關鍵之處和難點所在,因此需要對模式進行文件化的工作,甚至在必要的時候,對這部分的設計進行培訓和指導。確保你的團隊能夠正確的使用文件來設計、理解、擴充套件模式。我們在解決方案的前乙個部分提到了盡可能延遲文件的建立。而在設計重構為模式的時候,我們就需要進行文件化的工作了。因為模式具有靈活性,能夠抵抗一定的變更風險。
重構並保持模式的一致性
正如上一節所說的那樣,模式並不是乙個很容易理解的東西,雖然它保持了設計的靈活性和穩定性。對於物件導向的新手而言,模式簡直就像是飛碟一樣,由於缺少物件導向的設計經驗,他們無法理解模式的處理思路,在實踐中,我們不只一次的碰到這種情況。我們不得不重頭開始教授關於模式的課程。因此,最後我們在軟體設計採用一定數量的模式,並確保在處理相同問題的時候使用相同的模式。這樣,應用的模式就成為解決某一類的問題的標準做法,從而在一定程度上降低了學習的曲線。
保持模式的一致性的另乙個方面的含義是將模式作為溝通的橋梁。軟體開發是一種團隊的行為。因此溝通在軟體開發中扮演著很重要的角色。試想一下,開發人員在討論軟體設計的時候,只需要說"使用工廠模式",大家就都能夠明白,而不是費勁口舌的說明幾個類之間的關係。這將大大提高溝通的效率。此外,模式的使用和設計的重構對於提高團隊的程式設計水平,培養後備的設計人員等方面都是很有意義的。
好文分享 簡單 快樂
我建立這個部落格,旨在感受一下極簡主義的生活方式所帶來的快樂和挑戰。我堅信,簡簡單單地生活可以更快樂。我想抓住 恰到好處 那令人難以捉摸的真 諦。同時,我也希望找乙個地方,讓我的志同道合者共同分享他們的酸甜苦辣,挫折抑或成功的點點滴滴。你也許會想,我不可能成為乙個極簡主義者 我養寵物 我帶著孩子 我...
Android架構重構
我將專案分為了四個層級 模型層 介面層 核心層 介面層。模型層定義了所有的模型 介面層封裝了伺服器提供的api 核心層處理所有業務邏輯 介面層就處理介面的展示。幾個層級之間的關係如下圖所示 下面展開說明具體的每個層次 介面層封裝了網路底層的api,並提供給核心層呼叫。剛開始,為了簡單,該層的核心類我...
Chatter 系統架構重構 Tips
這篇文章用來記錄重構系統架構時,遭遇到的問題內容,以及當下處理的解決方案。應該會不定時更新 xd 2012 07 03 命名規則定義 問題內容 命名規則沒有標準化規範,維護程式 困難。解決方案 採用微軟標準命名規則,提高程式 質量。系統架構分層 問題內容 架構分層不明確職責散落各處,容易改東壞西。解...