架構之美第二章

hzbook2008發表於2009-11-26

2 章

    兩個系統的故事:現代軟體神話

    Pete Goodliffe

    架構是一種很浪費空間的藝術。

    —Philip Johnson

    軟體系統就像一座由建築和後面的路構成的城市—由公路和旅館構成的錯綜複雜的網路。在繁忙的城市裡發生著許多事情,控制流不斷產生,它們的生命在城市中交織在一起,然後死亡。豐富的資料積聚在一起、儲存起來,然後銷燬。有各式各樣的建築:有的高大美麗,有的低矮實用,還有的坍塌破損。隨著資料圍繞著它們流動,形成了交通堵塞和追尾、高峰時段和道路維護。軟體之城的品質直接與其中包含多少城市規劃有關。

    某些軟體系統很幸運,建立時由有經驗的架構師進行了深思熟慮的設計,在構建時體現出了優雅和平衡,有很好的地圖,便於導航。另一些軟體系統就沒有這麼幸運,基本上是一些透過偶然聚集的程式碼漸漸形成的,交通基礎設施不足,建築單調而平凡,置身於其中時會完全迷失,找不著路。

你的程式碼願意待在怎樣的“城市”中?你願意構建哪一種城市?

    在本章中,我將講述這樣兩個軟體城市的故事。這是真實的故事,就像所有好的故事一樣,這個故事最終是有教育意義的。人們說經驗是偉大的老師,但最好是別人的經驗,如果你能從這些專案的錯誤和成功之中學習,你(和你的軟體)可能會避免很多的痛苦。

    本章中的這兩個系統特別有趣,因為它們有很大不同,儘管從表面上看非常相似:

     ... ...(文字不清晰)

    在這個故事中,人名都已改變,目的是保護那些無辜的人(和有罪的人)。

    2.1   混亂大都市

你們修築、修築,預備道路,將絆腳石從我百姓的路中除掉。

    —《以賽亞書》第57章14節

    我們要看的第一個軟體系統名為“混亂大都市”。它是我喜歡回顧的一個系統—既不是因為它很好,也不是因為它讓參與開發的人感到舒服,而是因為當我第一次參與它的開發時,它教給了我有價值的軟體開發經驗。

我第一次接觸“混亂大都市”,是在我加入了建立它的公司時。初看上去這是一份有前途的工作。我將加入一個團隊,參與基於Linux的、“現代”的C++程式碼集開發,已有的程式碼集已經開發幾年了。如果你像我一樣擁有特殊的技術崇拜,就會覺得很興奮。

    工作起初並不順利,但是你不能指望在加入一個新團隊、面對新的程式碼集時會覺得很輕鬆。然而,日復一日(周復一週),情況卻沒有任何好轉。這些程式碼要花極長的時間來學習,沒有顯而易見的進入系統中的路徑。這是個警告訊號。從微觀的層面來說,也就是從每行程式、每個方法、每個元件來看,程式碼都是混亂而粗糙地壘在一起的。不存在一致性、不存在風格、也沒有統一的概念能夠將不同的部分組織在一起。這是另一個警告訊號。系統中的控制流讓人覺得不舒服,無法預測。這又是一個警告訊號。系統中有太多的“壞味道”(Fowler 1999),整個程式碼集散發著腐爛的氣味,是在大熱天裡散發著刺激性氣體的一個垃圾堆。這是一個清晰的警告訊號。資料很少放在使用它的地方。經常引入額外的巴羅克式快取層,目的是試圖讓資料停留在更方便的地方。這又是一個警告訊號。

    當我試圖在大腦中建立“大都市”的全圖時,沒有人能解釋它的結構;沒有人知道它的所有層、它的藤蔓,以及那些黑暗、隔離的角落。實際上,沒有人知道它究竟有多少部分是真正能工作的(它實際上靠的是運氣和英雄式的維護程式設計師)。人們知道他們面對的那一小部分割槽域,但沒人瞭解整個系統。很自然,沒有任何文件。這也是一個警告訊號。我需要的是一份地圖。

     這是一個悲傷的故事,我曾是其中的一部分:“大都市”是城市規劃的惡夢。在你開始整治混亂之前,先要理解混亂,所以我們花了很大的精力和毅力,得到了一份“架構圖”。我們標出了每一條公路、每一條主幹道、每一條很少人瞭解的小路、所有燈光昏暗的輔路,並將它們畫在一張主圖上。我們第一次看到了這個軟體的樣子,並不令人賞心悅目。它是一些混亂的區塊和線條。為了讓它更好理解一些,我們用顏色標出了控制路徑,突出了它們的型別。然後我們後退一步看著它。

     它令人吃驚。它令人目眩神迷。它就像一隻喝醉了的蜘蛛,跌進了一些海報顏料罐裡,然後在一張紙上織成了一張彩色的網。它看起來就像圖2-1那樣(這是一個簡化後的版本,細節已經修改了,為了保護那些有罪的人)。事情變得很清楚了。我們畫出了倫敦地鐵圖。它甚至有環線。

2-1:“混亂大都市”的“架構”

    這就是那種讓跑遍各地的銷售員惱怒的系統。實際上,它與倫敦地鐵的相似性讓人印象深刻:從系統的一端到另一端有很多條路線,哪條路最好通常是不明顯的。地理位置很近的目的地常常很難到達,你希望能在兩點之間再挖掘一條隧道。有時候,走出地鐵換乘巴士實際上是更好的選擇。或者乾脆步行。

    無論從哪個角度來看,這都不是一個“好的”架構。“大都市”的問題超出了設計的範疇,它涉及開發過程和企業文化。這些問題實際上導致了許多架構腐爛。程式碼經過幾年的“有機”生長,沒有人進行過任何架構設計,而且各個部分是隨著時間推移,沒有經過太多思考就栓在一起的。我們這麼說真的算是客氣的了。沒有人停下來為程式碼建立一個理智的結構。它增長、膨脹,成為絕對沒有任何架構設計的系統的一個典型。程式碼集從來不會沒有架構。這個系統只是擁有一個很糟糕的架構。

    如果我們回顧建立“大都市”的公司的歷史,它所處的狀態是可以理解的(但是不可寬恕):這是一個初創的公司,快速提供許多新版本的壓力很大。延期是不可容忍的—這會帶來財務災難。軟體工程師被迫盡其極限,快速交付。所以程式碼是以一系列瘋狂衝刺的方式壘在一起的。

注意:不好的公司結構和不健康的開發過程將在糟糕的軟體架構中得到反映。

     2.1.1   後果

    “大都市”缺少城市規劃,這帶來了許多後果,我們將在這裡進行分析。這些後果的影響是很嚴重的,遠遠超出了你對不良設計的天真想象。地鐵變成了雲宵飛車,飛速地朝下猛衝。

    不可理解

    正如你已經看到的,“大都市”的架構以及缺乏強制的結構,導致了一個很難理解的軟體系統,實際上幾乎不可能修改。新加入專案的團隊成員(譬如我)會被複雜性驚呆,不能夠搞清楚狀況。

    壞的設計確實會招致在它上面疊加壞的設計(實際上它簡直就是迫使你那樣做),因為沒有一種明智的方式可以擴充套件該設計。在所有能解決手上工作的方法之中,阻力最小的總會被採用,沒有明顯的辦法來修復這些結構問題,所以只要能減少麻煩,就會扔進去新的功能。

注意:重要的是要保持軟體設計的品質。壞的架構設計會招致更壞的架構設計。

     缺乏內聚

     系統的元件完全沒有內聚性。每個元件本來都應該有一個定義良好的角色,但是它們卻包含了一堆雜亂的、不一定相關的功能。這使我們很難確定元件存在的原因,也很難弄明白系統中已經實現了哪項具體的功能。

很自然,這讓缺陷修復成為了一場噩夢,嚴重地影響了軟體的品質和可靠性。

    功能和資料都放在了系統中錯誤的地方。許多你認為是“核心服務”的部分卻沒有在系統的核心部分實現,而是由邊遠的模組來模擬實現(非常痛苦並且代價很大)。

進一步的軟體歷史考察揭示了原因:原來的團隊中存在個人鬥爭,所以一些關鍵程式設計師開始建立他們自己的軟體小帝國。他們把自己認為酷的功能放到他們的模組中,即使它不應該屬於那裡。為了做到這一點,他們於是又實現了更為巴羅克式的通訊機制,把控制連回到正確的地方。

注意:開發團隊中健康的工作關係將直接有益於軟體設計。不健康的關係和個性膨脹會導致不健康的軟體。

內聚和耦合

    軟體設計的關鍵品質是內聚和耦合。這不是什麼新奇的“物件導向”概念;自從20世紀70年代出現結構化設計開始,開發者對這一概念已經談論了許多年。我們的目標是透過設計使系統的元件具備下列品質:

    *   高內聚(Strong cohesion)

內聚是一個測量指標,說明相關的功能如何聚集在一起,模組內的各部分作為一個整體工作得如何。內聚性是將模組粘成一個整體的膠水。

    弱內聚的模組是不良分解的訊號。每個模組都必須具有清晰定義的角色,而不只是一堆不相關的功能。

    *   低耦合(Low coupling)

    耦合是模組之間獨立性的測量指標—它們之間進出“電線”的數量。在最簡單的設計中,模組幾乎沒有什麼耦合,所以彼此間的依賴關係較少。顯然,模組不能夠完全解耦,否則它們將根本不能夠一起工作!

模組之間的聯絡有多種方式,有的是直接的,有的是間接的。模組可以呼叫其他模組中的函式,或被其他模組所呼叫。它可能使用其他模組提供的Web服務或設施,可能使用其他模組的資料型別,或提供某些資料讓其他模組使用(可能是變數或檔案)。

    好的軟體設計會限制通訊的線路,只提供那些絕對需要的。這種通訊線路是確定架構的一部分因素。

不必要的耦合

   “大都市”沒有清晰的分層。模組之間的依賴關係不是單向的,耦合常常是雙向的。元件A會到達元件B的內部,目的是完成它的一項任務。在其他的地方,元件B又透過硬編碼呼叫了元件A。系統沒有最底層,也沒有控制中心。它是整體式的一大塊軟體。

    這意味著系統的各部分之間耦合非常緊密,你想啟動系統骨架就不得不建立所有的元件。單個元件的任何改變都會波及其他元件,需要修改許多依賴它的元件。孤立地看程式碼元件沒有任何意義。

    這使得低層次的測試不能夠進行。不僅是程式碼層次的測試不可能進行,而且元件層次的整合測試也不能夠建立,因為每個元件都依賴於幾乎所有其他元件。當然,在公司中,測試從來也不具有很高的優先順序(我們根本沒有時間來做這種測試),所以這“不成為問題”。不必說,這個軟體不太可靠。

    注意:好的設計考慮到內部元件連線的連線機制和連線數(以及連線性質)。系統的單個部分應該能夠獨立存在。緊耦合將導致不可測試的程式碼。

程式碼問題

    不良的頂層設計所帶來的問題也影響到了程式碼層面。問題會引起其他問題(參見Hunt 和Davis[1999]中關於破窗理論的討論)。因為沒有通用的設計,也沒有整體專案“風格”,所以也沒有人關心共同的編碼標準、使用共同的庫,或採用共同的慣例。元件、類和檔案都沒有命名慣例。甚至都沒有共同的構建系統。膠帶、Shell指令碼、Perl膠水與makefile和Visual Studio專案檔案混在一起。編譯這個怪物被視為一場複雜的成人儀式!

   “大都市”最微妙而又最嚴重的問題是重複。由於沒有清晰的設計,也不清楚功能應該處於的位置,所以輪子在整個程式碼集中不斷重新發明。一些簡單的東西,如通用演算法和資料結構,在許多模組中重複出現,每種實現都帶有自己的一些未知的缺陷和怪異的行為特徵。更大範圍的關注點,如外部通訊和資料快取,也實現了許多次。

    更多的軟體歷史考察揭示了原因:“大都市”開始是從一系列獨立的原型拼起來的,這些原型本該拋棄。“大都市”實際上是偶然形成的城市群。當程式碼元件縫合在一起時,元件之間匹配得不好。隨著時間的推移,這種隨意的縫合開始破裂,所以元件互相拉扯,導致程式碼集破碎,而不是和諧地協作。

注意:鬆弛而模糊的架構將導致每個程式碼元件編寫得不好,並且相互之間匹配得不好。它也會導致重複的程式碼和工作。

程式碼以外的問題

   “大都市”內部的問題已經超越了程式碼集,在公司中其他的地方導致了混亂。不僅開發團隊中有問題,而且架構的腐爛也影響到了支援和使用該產品的人。

開發團隊

    專案的新成員(例如我)被複雜性驚呆了,不能夠搞清楚狀況。這很好地解釋了為什麼很少有新人能在公司裡待下來—員工流失率非常高。

    那些留下來的人非常努力地工作,專案的壓力非常大。規劃新的功能會導致極大的恐懼。

緩慢的開發週期

    由於維護“大都市”是一項恐怖的任務,所以即使是最簡單的變更或“很小的”缺陷修復都不知道要花多少時間。管理軟體開發週期非常難。客戶只好等著實現重要的特徵,管理層對開發團隊不能滿足業務目標感到越來越沮喪。

支援工程師

    在支援這個極不尋常的產品時,產品支援工程師度過了可怕的時光,他們要設法弄明白很小的軟體版本差異之間錯綜複雜的行為差異。

    第三方支援

    專案開發了一個外部支援協議,支援其他裝置遠端控制“大都市”。由於它只是軟體內部結構上面薄薄的一層,所以它反映了“大都市”的架構,這意味著它也是巴羅克式的、難以理解的、容易偶爾出錯的、不可能使用的。第三方工程師的生活也被“大都市”的可怕結構搞得一團糟。

    公司內部政治

    開發問題導致了公司內部不同“種族”的分裂。開發團隊與營銷銷售團隊之間關係緊張,每次新版本要推出時,製造部門總是要承受巨大的壓力。經理們已經絕望了。

注意:不良架構的影響不僅限於程式碼。它會進一步影響到人、團隊、過程和時間表。 

    清晰的需求

    軟體歷史考察凸顯了“混亂大都市”之所以混亂的一個重要原因:在專案開始之初,團隊並不知道要構建的是什麼。

本來的初創公司知道它要佔領哪個市場,但不知道哪種產品能佔領這個市場。所以他們兩面下注,要求一個可以做許多事情的軟體平臺。噢,我們昨天就想得到它了。所以程式設計師們急急忙忙建立了一個毫無希望的總體基礎設施,它具有做任何事情的潛力(但做得不好),而不是建立一個把一件事情做好的架構,並能夠在將來進行擴充套件,做更多的事情。

注意:重要的是要在開始設計系統之前知道你打算設計什麼。如果你不知道它是什麼,也不知道它將做什麼,暫時不要開始設計它。只設計你知道需要的東西。

在規劃“大都市”的早期階段,有太多的架構師。面對糊塗的需求,他們都拿著一塊拼不起來的拼圖,試圖獨自工作。他們在工作時沒有考慮到整個專案,所以當他們試圖將這些拼圖拼在一起時,就拼不起來了。沒有時間進一步思考架構,軟體設計的各個部分有一些重疊,於是開始了“大都市”的城市規劃災難。

    2.1.2   現狀

   “大都市”的設計幾乎完全是無可救藥的—相信我,隨著時間的推移,我們也嘗試過修復它。修復工作需要返工、重構、修改程式碼結構中的問題,這些已經成為不可能的任務。重寫也不是省事的方案,因為支援老的、巴羅克式的控制協議是需求的一部分。

    你可以看到,“大都市”的“設計”產生的後果是殘酷的,並且會無情地變得更糟。很難加入新的特性,所以人們只是忙著新增更多不完善的功能、救急補丁和編造的謊言。沒有人在面對程式碼時感到愉快,專案正盤旋著向下栽。缺乏設計導致了不良的程式碼,從而又導致了不良的團隊精神和不斷變長的開發週期。這最終導致了公司嚴重的財務問題。

最後,管理層宣佈“混亂大都市”已經不盈利了,它被拋棄了。對於任何組織機構來說,這都是勇敢的一步,特別是這個公司一直都眼高手低,同時又試圖避免沉淪。帶著團隊從以前版本中得到的所有C++和Linux經驗,他們在Windows上用C#重寫了系統。猜猜看會怎麼樣。

    2.1.3   來自“大都市”的名信片

    那麼我們學到了什麼?不良的架構會產生深遠的影響和嚴重的反彈。在“混亂大都市”中缺少預見性和架構設計,導致了下面的問題:

   ... ...(文字不清晰) 

    2.2   設計之城

    形式永遠服從功能。

    —Louis Henry Sullivan

   “設計之城”軟體專案表面上與“混亂大都市”非常相似。它也是用C++寫的消費音訊產品,執行在Linux作業系統上。但是,它的構建方式有很大不同,所以內部結構也非常不同。

    我從一開始就參加了“設計之城”專案。我們用有能力的開發者組成了一個全新的團隊,從頭開始構建這個產品。團隊很小(開始有4名程式設計師),像“大都市”專案一樣,團隊的結構是扁平的。幸運的是,沒有出現“大都市”專案中的個人對抗,在團隊中也沒有出現任何爭權奪利的事。在此之前,團隊成員之間並不非常熟悉,不知道我們在一起可以配合得多好,但我們對這個專案都很熱心,喜歡這項挑戰。

這樣很好。

    Linux和C++是專案早期的決定,這項決定確定了團隊成員的組成。從一開始,專案就有清晰定義的目標:具體的首個產品和將來功能的路線圖,程式碼集必須能夠支援這些功能。這將是一個通用目標的程式碼集,可以適用於多種產品配置。

開發過程採用了極限程式設計(XP)(Beck和Andres 2004),很多人相信這種開發過程避開了設計:直接開始編碼,不要想太遠。實際上,一些旁觀者對我們的選擇感到震驚,並預言專案將以淚收場,就像“大都市”一樣。但這是一種常見的誤解。XP沒有貶低設計,它貶低的是不必要的工作(即YAGNI原則,You Aren誸 Going To Need It)。但是,如果需要前端設計,XP就要求你進行設計。它也鼓勵使用快速原型(所謂的“spike”),快速展現並驗證設計的有效性。這些都非常有用,對最終的軟體設計產生了極大的影響。

    2.2.1   設計之城的第一步

    在設計過程的早期,我們確定了主要的功能領域(這包括核心的音訊通道、內容管理和使用者控制/介面 )。我們考慮了它們如何在系統中適配,推出了初步的架構,包括了實現效能需求所必需的核心執行緒模型。

    系統中各獨立部分的相對位置關係體現為傳統的分層結構,圖2-2展示了簡化後的結果。請注意,這並不是龐大的前端設計。它是有意為之的“設計之城”的簡單概念模型:圖中只有一些大塊,這是一個基本的系統設計,可以隨著功能模組的新增而輕鬆地增長。雖然很基本,但這個初始架構為增長提供了堅實的基礎。“大都市 ”沒有總體規劃,在“方便”的地方嫁接(或修補)功能。

    我們在系統的核心上花了額外的設計時間:音訊通道。它實際上是整個系統的一個內部子架構。為了確定它,我們考慮了穿過一系列元件的資料流,最後得到了一個“過濾器和管道”音訊架構,如圖2-3所示。根據產品的不同物理配置,它包含了這樣一些管道。同樣,開始時這些管道只是一個概念,即圖中的一些方塊。我們當時還沒有決定如何將所有模組拼裝在一起。

    圖2-2:“設計之城”的初始架構

    圖2-3:“設計之城”的音訊管道

我們在早期也選擇了專案將採用的支援庫(例如,可以從獲得的Boost C++庫和一組支援資料庫的庫)。關於一些基本關注點的決定是這時候做出的,目的是確保程式碼能夠容易而一致地增長,這些決定包括:

    ... ...(文字不清晰)

    這些“細節”完美的因素非常重要:它們與軟體架構密切相當,影響到後來的許多決定。

    2.2.2   故事展開

    在團隊完成了初始設計之後,“設計之城”專案按照XP過程推進。設計和編碼要麼以結對的方式完成,要麼經過仔細的複審,確保工作的正確性。

    隨著時間的推移,設計和程式碼不斷髮展和成熟;隨著“設計之城”的故事逐漸展開,產生了下面的結果。

定位功能

    由於從一開始我們就有系統結構的清晰總體檢視,所以新的功能單元可以一致地新增到程式碼集的正確功能區域。程式碼應該屬於哪一塊從來就不是問題。在擴充套件功能或修復問題時,我們總是很容易找到已有功能的實現程式碼。

現在,把新的程式碼放到“正確”的位置有時候比簡單“嫁接”到方便而不妥的地方而更難一些。所以,架構規劃的存在有時候讓開發者的工作變得更難一些。這些額外工作的回報就是今後的生活要容易很多,當我們維護或擴充套件系統時,不愉快的事情會很少。

    注意:架構有助於定位功能:新增功能、修改功能或修復缺陷。它為你提供了一個模板,讓你將工作納入到一張系統導航圖中。

    一致性

    整個系統是一致的。各個層次的所有決定都是在整個設計的背景下做出的。開發者從一開始就有意為之,這樣得到的所有程式碼都完全符合系統設計,並與編寫的所有其他程式碼相匹配。

     在專案的歷史中,儘管有許多變更,涉及程式碼集的各處(從單行程式碼到系統結構),但這些變更都符合最初的設計模板。

     注意:清晰的架構設計將導致一致的系統。所有決定都應該在架構設計的背景下做出。

頂層設計的好風格和優雅很自然會為較低的層帶來好處。即使在最低層,程式碼也是統一而整潔的。清晰定義的軟體設計確保了沒有重複,熟悉的設計模式到處使用,熟悉的介面慣例普遍採用,沒有特殊的物件生命週期或奇怪的資源管理問題。程式碼是在城市規劃的背景之中寫成的。

    注意:清晰的架構有助於減少功能重複。

    架構的增長

    有一些全新的功能領域出現在了設計“全圖”中,例如儲存管理和外部控制功能。在“大都市”專案中,這是致命的一擊,難度超乎想象。但在“設計之城”專案中,事情就不一樣了。

    系統設計就像程式碼一樣,被認為是可擴充套件、可重構的。開發團隊的一項核心原則就是保持敏捷,沒有什麼是一成不變的,所以在需要時架構也可以修改。這促使我們讓設計保持簡單並易於修改。這樣一來,程式碼可以快速地增長,同時又保持好的內部結構。新增新的功能塊不是問題。

注意:軟體架構不是一成不變的。需要時就改變它。要想做到可以修改,架構就必須保持簡單。犧牲簡單性的修改要抵制。

    延遲設計決定

    有一項XP原則確實提高了“設計之城”的架構品質,這就是YAGNI(如果你不是馬上需要,就不要去做)。這促使我們在早期只設計了重要的部分,將所有餘下的決定推遲,直到我們對實際的需求有了更清晰的理解並知道如何放到系統中最好時,再做出這些決定。這是一種非常強大的設計方法,在很大的程度上解放了我們的思想。

    ... ...(文字不清晰)

   這些過程確保了系統中從未加入不正確的、不合適的變更。所有不符合軟體設計的內容都被拒之門外。這可能聽起來有點過於嚴厲,但這些是開發者們堅信的過程。

這種信念凸顯了一個重要的態度:開發者們相信設計,認為設計對專案相當重要。他們擁有設計,對設計負責。

   注意:必須保持架構品質。只有當開發者們相信它並對它負責時,才能做到這一點。

管理技術債務

   除了這些品質管理方法之外,“設計之城”的開發是相當注重實效的。隨著最後期限的臨近,一些不太重要的功能被砍掉,讓產品能夠準時推出。小的程式碼“瑕疵”或設計問題允許存在於程式碼集中,要麼是為了讓功能快一點實現,要麼是為了在接近釋出時避免高風險的改動。

    但是,與“混亂大都市”專案不同的是,這些逃避職責的地方被標記為技術債務,並安排在後續的版本釋出中修正。這些問題很清楚,開發者對它們不滿意,直到將它們處理掉為止。同樣,我們看到了開發者對設計的品質負責。

    單元測試打造了設計

    關於程式碼集的一項核心決定就是所有程式碼都要有單元測試(這也是在XP開發中強制要求做到的)。單元測試帶來了許多好處,其中一點就是能夠修改軟體的一些部分,而不必擔心在修改的過程中破壞其他的東西。我們對“設計之城”內部結構的某些部分進行了相當激進的返工,單元測試給了我們信心,讓我們相信系統的其他部分沒有被破壞。例如,執行緒模型和音訊管道的內部連線介面都進行了徹底的改變。這是在子系統開發較晚的時候發生的嚴重設計變更,但與音訊通道介面的其他程式碼仍然執行得很好。單元測試讓我們能夠改變設計。

    隨著“設計之城”的逐漸成熟,這種型別的“主要”設計變更越來越少了。在經過一些設計返工之後,情況穩定下來,此後只有一些不重要的設計變更。系統開發得很快:以迭代的方式進行,每一次迭代都改進了設計,直到它達到了相對穩定的狀態。

    注意:你的系統應該有一組不錯的自動化測試,它們讓你在進行根本的架構變更時風險最小。這為你提供了工作的空間。

    單元測試的另一個主要好處在於,它們在很大程度上定型了程式碼設計:它們實際上迫使我們實現好的結構。每個小的程式碼元件都被定型成定義良好的實體,可以獨立存在,因為它必須能夠在單元測試中構造出來,不需要圍繞它構造系統的其他部分。編寫單元測試確保了每個程式碼模組的內聚性,也確保了與系統其他部分之間的松耦合。單元測試迫使我們仔細考慮每個單元的介面,確保該單元的API是有意義的,內部是一致的。

    注意:對你的程式碼進行單元測試將帶來更好的軟體設計,所以設計時要考慮可測試性。

    設計時間

   “設計之城”成功的另一個因素是分配的開發時間段,它既不長也不短(就像金髮歌蒂的粥,既不熱也不冷,剛剛好)。專案需要一個有利的環境才能獲得成功。

如果時間太多,程式設計師常常會想建立他們的鉅作(那種總是快要好了,但永遠不會實現的東西)。有一點壓力是好事,緊迫感有助於完成事情。但是,如果時間太少,就不可能得到任何有價值的設計,你只會得到半生不熟的解決方案,就像“大都市”那樣。

   注意:好的專案計劃將帶來優質的設計。分配足夠的時間來建立架構傑作,它們不會立即出現。

   與設計同行

   儘管程式碼集很大,但它是一致而易於理解的。新的程式設計師可以比較容易地拿起程式碼並開始工作。不需要去理解不必要的複雜內部關係,也不需要面對奇怪的遺留程式碼。

由於程式碼中產生的問題比較少,工作起來有樂趣,所以團隊人員的流失率很低。這是因為開發者們負責設計,並不斷希望改進它。

    看著開發團隊動態地遵守架構設計是一件有趣的事情。“設計之城”的專案原則規定沒有人“擁有”哪一部分設計,這意味著任何開發者都可以改動系統的所有地方。每個人都應該寫出高品質的程式碼。“大都市”是許多不協作的、互相爭鬥的程式創造的一團混亂,而“設計之城”則是由密切合作的同事建立的一組乾淨、一致、密切合作的軟體元件。在很大程度上,Conway法則(注)反過來也生效,團隊的組織方式就像軟體的組織方式一樣。

    注意:團隊的組織方式必然對它產生的程式碼有影響。隨著時間的推移,架構也會影響到團隊協作的好壞。當團隊瓦解時,程式碼的互動就很糟糕。當團隊協作時,架構就整合得很好。

    2.2.3   現狀

    在一段時間之後,“設計之城”的架構如圖2-4所示。也就是說,它與最初的設計非常相似,同時也包含了一些值得注意的變更。此外,它還包含了大量的經驗,證明這個設計是正確的。健康的開發過程,小的、更善於思考的開發團隊,適當注意確保一致性,帶來了極為簡單、清晰、一致的設計。這種簡單性為“設計之城”帶來了好處,得到了可擴充套件的程式碼和快速開發的產品。

    在編寫本書時,“設計之城”專案已走過了3年。程式碼集仍在使用,而且擴充套件出了一些成功的產品。它還在開發、成長、擴充套件,還在每天發生變化。下一個月它的設計可能與這個很不同,但也可能沒有不同。

    我要澄清一點:這些程式碼並不完美。有些地方存在著技術爭論,但是它們在整潔的背景下顯得特別突出,會在將來得到解決。沒有什麼是一成不變的,由於適應性強的架構和靈活的程式碼結構,這些問題都可以解決。幾乎所有東西都各就各位,因為架構很好。

     圖2-4:“設計之城”的最終架構

     2.3   說明什麼問題

    等那完全的來到,這有限的必歸於無有了。

    —《哥林多前書》第13章10節

    這個關於兩個軟體系統的簡單故事當然不是軟體架構的全面介紹,但我已展示了架構如何對軟體專案產生深遠的影響。架構幾乎會影響所有與之相關的人和事,它決定了程式碼集的健康,也決定了相關領域的健康。就像一個繁榮的城市會為當地帶來成功和聲望,好的軟體架構將幫助專案獲得發展,為依賴於它的人帶來成功。

    好的架構是很多因素的結果,包括以下方面(但不限於此):

   ... ...(文字不清晰)

    2.4   輪到你了

   絕不要失去神聖的好奇心。

   —阿爾伯特·愛因斯坦

   你正在讀這本書是因為你對軟體架構感興趣,而且你對改進自己的軟體感興趣。所以這裡就有一個極好的機會。對於你目前的軟體經驗,請考慮以下簡單的問題:

    ... ...(文字不清晰)

   參考文獻

   Beck, Kent, with Cynthia Andres. 2004. Extreme Programming Explained, Second Edition. Boston, MA: Addison-Wesley Professional.

   Fowler, Martin. 1999. Refactoring: Improving the Design of Existing Code. Boston, MA: Addison-Wesley Professional.

   Hunt, Andrew, and David Thomas. 1999. The Pragmatic Programmer. Boston, MA: Addison-Wesley Professional.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16502878/viewspace-620673/,如需轉載,請註明出處,否則將追究法律責任。

相關文章