SDWest2006(譯注1)對我來說是個有趣的大會。我除了星期三之外(當時我正飛往費城參加一個客戶會議 == 因此錯過了Jolt頒獎部分)每天都在演講。我也參加了一些談話和會議;其中最引人關注的是Mike Cohn的計劃與估算的談話。我的兩個談話都是半天的關于Ood原則的導引。這些談話都參與的非常好,現場反映也很熱烈。
這里是我談話的幾份演講稿:
- 類設計之高級原則??? 在OO設計中,掌控類間組合或是關聯關系的原則到底是什么?
- 組件設計之高級原則? 在大型OO設計中,你該如何掌控組件的組合或關聯關系?
- 首要導向因素????????專業與否的最低界限是什么?
在星期二我主導了一個關于“敏捷與架構設計”的圓桌會議。很多人是擠著進入會議現場的,而且一直參與到會議結束。配合著討論要點的導航圖,我們將這些要點一一討論過。我的好朋友John Kern也在那兒,還幫助解答了很多提問。你可以從這里了解更多有關這個會議的。
會議的一些關鍵要點如下:
- 架構設計的主要目標是靈活性,可維護性和可擴展性。
- 但我們認識到,由測試驅動開發原則指導產生的單元測試和驗收測試(譯注2)要比靈活性,可維護性和可擴展性來得更重要。
- 因而測試才是首要的影響力,而架構設計只是第二位的。
在此次會議前,我從未想到過這點。這里就有一群架構師和設計師,他們激烈的爭辯著架構設計的角色和位置,可是我們辛苦爭論出的一致結論是靈活性,可維護性和可擴展性是處于次要的影響作用的,是編寫測試(測試優先于產品代碼)起著最主要的效用。
這好像是吞下了苦黃蓮,而且很多架構設計師們搞不好立即就會拒絕。然而,沒人認為架構師不重要,或者認為應該丟掉它轉而去青睞測試。正相反,是測試讓我們無所顧慮的改進系統的設計。不是測試勝于架構設計;而應該說是測試成全了它!
自動化測試給了我們一個可靠的方式去了解系統是否運行良好。因此我們不必再懼怕一個設計的改變會破壞它。這讓實施那些能夠改進系統的設計變得更容易。因此測試的粉末登臺意味著在持續改進系統的結構、設計和架構時遇到的阻礙會來得更少。
失去測試,架構設計就是天方夜譚,因為一但重大的開發啟動后設計將很難改變。而自動化測試給設計留下了空間。設計改變所帶來的風險如此之大的被削減,以至于我們可以在肆無忌憚中就讓系統進化。
?
譯注:
1,SDWest,全稱Software Development West,即西部開發者大會,是全美頗受矚目的開發者大會,著名的Jolt大獎就在每年一度的大會上頒發。
2,驗收測試,原文acceptance test,又成為容忍測試,敏捷方法認為,如果一個構建(build)通過了驗收測試,基本上這個構建就可被“接受了”。
?
?(原文鏈接網址:http://www.butunclebob.com/ArticleS.UncleBob.ArchitectureIsaSecondaryEffect; Robert C. Martin的英文blog網址:?http://www.butunclebob.com/ArticleS.UncleBob)?
譯者注:Robert C. Martin是Object Mentor公司總裁,面向對象設計、模式、UML、敏捷方法學和極限編程領域內的資深顧問。他不僅是Jolt獲獎圖書《敏捷軟件開發:原則、模式與實踐》(中文版)(《敏捷軟件開發》(英文影印版))的作者,還是暢銷書Designing Object-Oriented C++ Applications Using the Booch Method的作者。Martin是Pattern Languages of Program Design 3和More C++ Gems的主編,并與James Newkirk合著了XP in Practice。他是國際程序員大會上著名的發言人,并在C++ Report雜志擔任過4年的編輯。
架構設計之性能設計經驗
??? 性能(performance)設計非常重要,對于服務器端實時交易系統來說系統性能的重要性不言而喻,對客戶端軟件來說性能好的軟件也會獲得良好的用戶體驗,從而給用戶留下高質量軟件的良好印象。因此在進行架構設計中性能設計非常重要。
??? 但架構設計實際是一個平衡設計,在可用性、可擴展性、可維護性、可靠性、高性能等之間做個妥協選擇。這些非功能性的需求再加上復雜的功能性需求,同時還要考慮到項目管理上tight schedule, low cost, perfect effect的三角難題約束,有時需求還不是很明確,vision不是很清楚,這種情況下系統架構設計真是一門藝術。
??? 單就性能設計來說,在架構設計初期就一定要把系統性能考慮在內,否則等開發完成以后測試發現性能不好就比較難辦,通常要花費較長的時間來診斷性能瓶頸,找到提升的辦法,甚至要改變架構,傷筋動骨,往往造成項目延期。所以性能設計首先要有明確的性能目標,根據用戶和軟件本身的性能要求來設計,合適的就是最好的。其次,要有適當的度量標準和量化的性能指標。最后,要有相應的設計策略,具體的測試方法。
??? 根據我的經驗,影響系統性能主要瓶頸在I/O,包括數據庫,socket,網絡通信,文件等,例如頻繁查詢數據庫并返回大量結果集,頻繁操作大文件等,這些昂貴的操作會占用大量的CPU時間。拿系統響應和服務一個事務來說,有幾個Round trip,要通過哪幾層I/O,如何合理的分配這些I/O的調用,降低不必要的I/O,都是進行系統性能設計要考慮的。而有些性能問題在初期并不會表現出來,但當拿到實際上線環境下,存在多用戶并發、大數據量的情況下就會暴露出嚴重的問題。所以性能設計時一定要考慮到I/O,同步,并發,資源爭用,以及大數據量等因素。通常,I/O操作、網絡響應、差的算法、數據庫、以及其他的低效的資源使用都會導致低劣的性能。
??? 具體可用的設計策略有:
??? - 緩存以及緩存層(caching layer)
??? 在數據層和應用層之間增加數據緩存層,提供全局數據服務。可以大大減少數據庫往返次數。與讀取數據庫和讀取大文件(如XML文件)比,讀取內存的速度無疑要快的多。所以對經常要訪問的數據進行緩存是非常好的實踐方法。因為現在系統往往內存很大,可以充分利用大內存,而共享內存更能實現數據并發訪問。
??? - 多線程(multi-threading)
??? 現在基本上大部分軟件實現多線程或多進程,多線程對單CPU系統還只是順序利用CPU時間和改善用戶體驗,多CPU系統才是真正的并行。要注意的是多線程不要爭搶訪問同一資源而導致部分串行操作,要做到真正的并行操作多線程并不容易。另外,在多線程間同步一個龐大的資源,過多創建線程又沒有實現線程池也會導致系統性能下降。
??? - 負載平衡(load balancing)
??? 物理上增加地位對等的集群服務器(Cluster),通過負載分配算法分配相應服務器來相應客戶端請求。很多系統支持負載均衡,Windows server2003 IIS就支持負載均衡服務,其他如WebLogic, WebSphere也有集群版本支持負載均衡。當然你也可以自己實現負載分配算法。
??? - 數據庫優化(database optimization)
??? 如果應用程序使用了數據庫,可以采取許多步驟來消除訪問和寫入數據時的瓶頸:
??? - 標識潛在的索引,但不要創建過多的索引。
??? - 如果使用 SQL Server,則使用 SQL Server 的事件探查器和索引優化向導。
??? - 監視處理器的使用;理想范圍是:75-80% 處理器時間。
??? - 使用查詢分析器分析查詢計劃以優化查詢。
??? - 使用存儲過程優化性能。
??? - 標準化寫入的大量數據 —寫入較少的數據。
??? - 取消標準化讀取的大量數據 —讀取較少的數據。
??? 文件系統優化
??? 有時候系統性能不好,但當你關閉寫log的功能,性能一下子提高很多。因為頻繁的打開關閉大log文件時I/O開銷非常大,同樣記錄log到數據庫也一樣。所以,release版盡量減少寫log,或干脆移到裸設備上。
??? 頻繁打開關閉文件對系統性能下降程度是驚人的,可以通過一些變通辦法來減少文件的頻繁操作。
??? 例如,原來的緩存持久化實現是保存在XML文件,每次要獲得一個配置項,都打開XML文件,通過XPath拿到這個配置項的值,這樣效率不高,而且容易把這個XML文件lock住;改進的方法是:通過比較XML文件的修改時間(System.IO.File.GetLastWriteTime)判斷是否要再次打開文件,大大提高了效率;另一個可以改進的方法是:啟動時讀取所有配置到一個靜態的HashTable,每次要獲得一個配置項都從內存HashTable獲取,在最后或適當的時候持久化到XML。
??? 代碼性能設計
??? 在編程實現上,代碼性能設計也很重要,一些昂貴的操作會占用大量的資源和CPU時間。例如,字符串相加沒用StringBuilder, 頻繁創建對象,差勁的排序或遞歸算法,過多的裝箱拆箱,過多的使用反射(Reflection),頻繁new HashTable或大的數組,用異常(Catch Exception)用做正常的邏輯,使用復雜的正則表達式,等等。具體可以參考《Effective C++》《Effective C#》等書籍。
??? 語言的選擇
??? 另外,語言選擇也很重要。比如相對于Java, C#, C++, 大多數OLTP系統用C語言效率高的多,因為在所有的高級程序設計語言中,C程序設計語言的運行效率是公認的。再比如我們熟悉的一些框架,框架本身是C#或是Java的,但其核心獨立模塊是C++封裝的,這樣可以達到最佳的性能。所以對于一些特定的業務需求目標和數據的具體情況,對于核心的模塊或算法,可以用特定的語言來實現以獲得更好的效率。
??? 應用層
??? 比如應用層和數據庫的API,在.Net中就有就有DataReader、DataSet和IList等的選擇以及轉換等,這個根據具體情況而定;還有就是大家常采用的數據的格式化和壓縮,以及采用分頁,減少傳輸的數據量;是否可以把一部分處理邏輯放在客戶端呢,減少服務端的工作量。界面端也是有很多針對性能優化的考慮,例如繪圖,控件重繪都是非常耗資源的,各控件的數據加載和數據綁定性能也各不相同,盡量采用惰性加載,異步加載;初始化和啟動速度等都是需要考慮和優化的。
架構設計的三個維度
架構設計是一個非常大的話題,不管寫幾篇文章,接觸到的始終只是冰山一角,更多的是實踐中去體會。
這篇文章主要介紹的是面向對象OO,面向方面AOP,面向服務SOA這三個要素在架構設計中的位置與作用。
一、架構設計三個維度
架構設計有三個維度,或者說是我們在考慮架構時需要思考的三個方向。分別為:面向對象、面向方面、面向服務。這三個維度可以看作是正交的,但不同維度會互相印證,互相支撐。
整個架構的示意圖如下所示:

二、面向對象
面向對象技術最初是從面向對象的程序設計開始的,它的出現以60年代simula語言為標志,并在Smalltalk語言的完善和標準化過程中得到更多的擴展和對以前的思想的重新注解。80年代中后期,面向對象程序設計逐漸成熟,被計算機界理解和接受,人們又開始進一步考慮面向對象的開發問題。直到現在,面向對象已經成為一種非常流行的編程方式,以及軟件設計的架構。
面向對象提出有三個主要目標:重用性、靈活性和擴展性,強調對象的“抽象”、“封裝”、“繼承”、“多態”。它能讓人們以更加接近于現實世界的方式來思考程序,這點可以說是面向對象最大的進步。
在OO思想的運用上,業界出現了很多好的經驗與技巧,從而涌現出大量的設計模式。可以說面向對象是系統分析與設計時的一個很重要的方面。
三、面向方面
面向方面最初來源于hook技術,本質上就是滿足擴展的需求,可以在程序中自由擴展功能。
面向方面不僅僅是一門編程技術,同樣也是一種架構設計的思路。如果說OO是縱向地分析、切割整個系統,那么可以認為AOP是橫向地對系統作切片。簡單地理解,OO與AOP分別從兩個不同的角度給我們提供了分析系統的思路。面向方面可以彌補面向對象的缺陷,兩種方式有機的結合在一起可以更加有效地分析系統。
我們認為OO是接近于人類認識自然的思維方式,但對于東方來說卻并不是這樣。當西方人看到一個復雜系統的時候,只會有一種思路,就是“分解”——將系統分解成一塊一塊,然后每個部分作研究。當東方人看到一個復雜系統的時候,更多地會關注系統中存在的關系,將系統作為一個有機的整體進行研究。
這兩種思維方式都沒有問題,結合起來的話分析問題解決問題會更好。面向對象與面向方面也同樣如此,都能對應到人類認識自然的思維方式上。不過中國人理解AOP可能會有不同的感悟——我寫的文章《讀易[13]·閑談中醫與AOP》有簡單的說明。
四、面向服務
面向服務可以說是最近炒得比較火的概念了。包括現在提到的SaaS——Software as a service,軟件即服務。準確說來,面向服務不僅僅是軟件行業的概念。這個要從社會的產業結構說起。
社會產業總共分為三個,第一產業農業,第二產業工業,第三產業服務業。最早社會的主要產業是第一產業農業,將近有幾萬年的歷史。十八世紀下半葉在英國開始的工業革命,對人們的生活產生了根本性的影響,社會的主要產業成了第二產業工業。
現在仍然屬于工業時代,或者有人說的“后工業時代”。而在后工業時代,社會的經濟體制必定要向第三產業服務業逐漸轉型。面向服務其實是社會經濟體制重心的一種遷移。
還是說回到軟件行業,社會的主要產業將轉變成服務業,自然軟件行業也會出現對應的變化,那就是這里提到的面向服務。面向服務今后會影響到軟件的交付模式,會對整個軟件行業的體制產生影響。
而說到架構層面,面向服務是系統發布功能的一種方式。并且基于這種方式下不同的系統之間能有效地通信、協作。常見的實現技術就是Web Service。
五、軟件全局觀
軟件架構設計的三個維度:面向對象、面向方面、面向服務。
最年長的一個維度就是面向對象,發展了好幾十年,也是相對來說比較成熟的一個維度。它解決的問題是系統內部結構的設計。
面向方面的思想提出來能夠彌補面向對象的缺陷。面向對象的方式不能實現橫切關注點的分離,而面向方面正是為了解決這個問題。面向方面與面向對象一樣都是解決系統內部結構的設計。
面向服務更多的是涉及到系統的外部,簡單地說就是發布功能。它并不關注系統內部結構的實現,所以說面向服務與面向對象或者面向方面并不沖突。
這三個維度并不是絕對孤立的,它們之間會互相影響、制約。我們在分析架構的時候需要同時考慮到這三個維度的問題。這樣有助于我們設計出更加優秀的架構。
軟件架構為誰而設計
(節選自《軟件架構設計》書稿)?
……如此看來,架構師應當為項目相關的不同角色而設計(如圖5-2所示):
l??????? 架構師要為客戶負責,滿足他們的業務目標和約束條件;
l??????? 架構師要為用戶負責,使他們關心的功能需求和運行期質量屬性得以滿足;
l??????? 架構師必須顧及處于協作分工“下游”的開發人員,
l??????? 架構師還必須考慮“周邊”的管理人員,為他們進行分工管理、協調控制、評估監控等工作提供清晰的基礎。
?
?
圖5-2???軟件架構師為誰而設計
?
一言以蔽之,軟件架構師必須做到內外兼顧、各層并重(如圖5-3所示)。只有這樣,軟件架構才能和它“包含了關于如何構建軟件的一些最重要的設計決策”的“地位”相符。
?
補充三點:
●這個話題我在2006IBM開發者大會的預熱課堂上有過演講,說明了如何運用基于多視圖的架構設計方法應對上述問題。
●另外可參考我在IBM DW上發的文章:運用RUP 4+1視圖方法進行軟件架構設計
●其實,《軟件架構設計》一書講述的具體方法和4+1方法有所不同……例如,明確引入“質量屬性分析”活動來為性能、可伸縮性、可重用性、可擴展性等非功能需求制定相應的架構決策。書的第15章專門介紹質量屬性分析(例如如何運用“質量-場景-決策”表這種思維工具落實需求、制定設計決策等)。
?