高手指點:如何取得好的軟件設計

>>>  技術話題—商業文明的嶄新時代  >>> 簡體     傳統

似乎作為一個軟件開發者,就注定要背著沉重的行囊,穿行在茂密的熱帶叢林里,酷熱,沒有風,只有腐爛的植被、濃濃的瘴氣、不時從肩膀上爬過的毒蜘蛛和從腳背上“嗖”地竄過的毒蛇。汗流浹背,疲憊不堪,卻不能休息片刻——因為這是一個軟件開發的混亂的時代!
充分的前期需求分析?嚴格的開發流程控制?嚴密的后期測試?這些都做到了啊,可是為什么軟件還是有bug?特別是用戶頻繁地提出新的要求,軟件要不斷地修改,每一次修改都膽戰心驚幾近崩潰,這到底是為什么?神啊,到底要怎么做才能獲得好的軟件設計啊?
人們一直都沒有停止思考如何能夠獲得好的軟件設計。軟件工程風行了幾十年,“沒有銀彈”的咒語還沒有解除。人們不禁要拋棄所有的“軟件工程”帶來的規制,回到軟件開發的源頭尋找答案。如下就是筆者看到的火燒云的形狀,像貓像虎任憑端詳。

1、要有技藝高超的開發者
軟件是人的思維活動的產物,軟件開發首先需要的是人的創造性。就如同其他一切人類創作一樣,人是占據核心地位的。恐怕再過一百年,也沒有哪個自動化的工序可以代替金庸先生的妙筆寫出《笑傲江湖》這樣經典的小說。所以,當前軟件工程企圖用其眼花繚亂的流程控制來達到“軟件工廠”的目的,是方向性的錯誤。人,只有人,技藝高超的開發者,才是產生好的軟件設計的核心因素。
技藝高超的開發者,應當有豐富的軟件開發經驗。有了豐富的軟件開發經驗,才會培養出對軟件設計中的各種“壞味道”的敏銳的嗅覺,才能熟練地繞過軟件設計中的種種陷阱;有了豐富的軟件開發經驗,才會對各種軟件體系結構模式爛熟于胸,才能運用成熟的設計模式解決那些普遍的設計問題;有了豐富的軟件開發經驗,才會對各種語言、工具的特長和限制了如指掌,才能選擇針對當前問題的最合適的編程語言和設計工具;有了豐富的軟件開發經驗,才會對各種平臺和環境的特性和差異有所了解,才能在設計的過程中最大程度地利用平臺和環境的特性,并可充分考慮到程序的可移植性。
技藝高超的開發者,應當有專業的領域知識。軟件總是服務于某一領域的應用,各個領域的專業特性不同,使得軟件開發也產生了許多分支。從事某一專業領域的軟件開發,必然要對該領域的專業知識有深入的了解,才能在軟件設計中正確地把握對概念的抽象。隨著軟件的復雜性的轉移,領域建模越來越受到重視。在此強烈推薦《領域驅動設計——軟件核心復雜性應對之道》(Eric Evans著,陳大峰等譯,清華大學出版社出版)一書。
技藝高超的開發者,應當有求真務實的技術作風。在這個浮躁、虛華、官僚化的社會里,“求真務實”顯得太可貴!這也是對開發者提出的很高的要求。求真務實的技術作風,就要求開發者不要盲從技術風潮,軟件開發的技術發展飛快,也產生了不同技術流派,各個流派都想擴大自己的影響,這時候就需要開發者自己去識破各種幌子,吸收真正對軟件開發有所助益的好方法;求真務實的技術作風,還要求開發者實事求是地看待各種非技術機制對軟件開發的影響,看看這些機制是否對軟件設計質量的提高有幫助,有則批判地采納,無則無情地拋棄,不要抱有“這個流程是某某認證所必須的,且其他很多公司都這樣做了,所以它一定有它的道理”的想法,這些目前看不到好處的東西,不要指望將來給你帶來什么好處;求真務實的技術作風,更要求開發者拋棄官僚主義作風和自大的姿態,在技術面前人人平等,以開闊的胸襟對待不同技術觀點和反對者,友好地同其他人合作,以把軟件設計做好為要旨。

技藝高超的開發者的成長需要長期的磨練,代價很大。且人的因素確實是一個很不確定(流動性很大)的因素,軟件工程減少不確定因素的初衷是好的,但是人的地位畢竟不可替代。“軟件藍領”是又一個軟件工程產生的畸形兒,軟件工程宣稱,只要幾個月的短期訓練,就可以勝任軟件開發的工作。真是可悲!

2、要有合理的軟件過程
軟件開發早已不是單兵作戰,而是一項有組織的集體活動,但是跟修水庫和建筑施工等“工程活動”不同,軟件開發需要集中團隊中每個人的智慧。軟件開發的過程中,最終要達到的目標在最初往往并不十分清楚,所以需要不斷地去探索。軟件開發的核心復雜性在于對應用領域的抽象與建模,很多時候,軟件開發也是一個建立模型、驗證模型、改進模型的一個不斷迭代的過程,所以一個合理的軟件過程至關重要。
一個合理的軟件過程,應該能獲取準確的需求。軟件需求據行業領域和客戶的要求不同而差異很大,沒有一個通行的模板可以套用,那么就需要對行業領域的充分研究以及與客戶的充分溝通來獲取到真實的需求。客戶把自己的需求表達清楚本身就是一件困難的事,開發者理解用戶的表達又增添了困難,語言會產生偏差,我們注重點的不同也會產生偏差所以往往我們“按照”客戶的需求做出來的東西并不是客戶真正想要的東西。再者,很多用戶的需求隨著時勢的變化會發生改變,這都是很正常的。因此,我們不能“苛求”一開始就能全盤獲得準確的需求,要想獲得準確的需求,就要保持一直與客戶的溝通,允許在我們的開發的過程中客戶提出變化,隨時表達和強調他們真正想要的“軟件”,只有這樣,我們的開發才不會偏離客戶的初衷,一切設計才有意義。
因此,合理的軟件過程應該將客戶需求的表達穿插在軟件過程的全部環節中,而不是指望在開始設計之前跟客戶訂好需求的“契約”就以為萬事大吉。
一個合理的軟件過程,應該快速的反饋軟件構思。軟件設計是開發者思維活動的產物,這種思維是建立在領域建模和軟件構建的基礎上的。建模的過程中,需要開發者極大地發揮自主性和創造性,不同的人,在不同的知識背景下思考的方式會不一樣,所以設計過程迫切需要驗證當前的思路是否正確,需要一些反饋信息來證明當前的模型是準確地滿足了某一需求。并且,構思是一個不確定、不嚴密的腦力活動,對開發者的經驗有很強的依賴性,而經驗的運用往往會產生不知不覺的錯誤或疏漏,所以需要及時的反饋來驗證構思的正確性,從而避免“錯之毫厘,謬以千里”。
快速的反饋是保證設計構思不會“離題”的必要手段,所以合理的軟件過程需要提供一個快速反饋的機制,而不是一味地想象,直到如夢初醒。
一個合理的軟件過程,應該保障團隊成員的溝通順暢。團隊成員合作開發,共同完成一個軟件系統,成員各自的思路之間是有“縫隙”的,要將每個人的設計聯結成一個整體的設計,就需要充分的溝通來填補這些“縫隙”只有充分的溝通,才能保證各個成員各自完成的部分組合起來的協調性,才能避免牛頭對個馬嘴。Brooks在其《人月神話》中曾描述過一種情況:往一個效率低下的軟件開發團隊中增加人員,不但不能提升其開發效率,反而會降低其開發效率。這說明團隊成員越少越好,同時也說明團隊成員之間溝通的時間耗費是巨大的。團隊成員交流的主要是對系統的理解和自己對模塊接口的劃分,因此需要的是直接的“對話”,是隨時展開的是“辯論”,是立竿見影的對設計者思路的沖擊,這樣才高效。而多余的文檔、圖表、流程或任何其他形式的溝通媒介,不僅耗費了開發者寶貴的時間,更可能引起傳遞“信號”的失真,帶來歧義。
一個合理的軟件過程,應該提倡“Work smart, not just hard.”(筆者借用這種說法,其本義可以參考http://www.jorydesjardins.com/pause/2006/05/what_does_that__1.html),軟件設計是一項智力勞動,其核心成果來自開發者發揮自己的聰明才智和創造性。(google就是一個很好的例子。)開發者的創造性來自對行業領域的鉆研和領域以外的借鑒,所以,開發者應當有豐富的領域外的知識的涉獵,在行業領域之外也有多姿多彩的閱歷和思考素材,發展自己的個性。這樣思路才不會呆板,思維才更活躍。
合理的軟件過程,應該給開發者營造一個輕松的工作氛圍,允許開發者的個性化,才能讓開發者身心愉悅地投入到軟件設計的虛空間中去,激發設計創意。

回頭審視一下傳統的“軟件工程”過程。在軟件工程中,十分強調“流程”和紀律,追求軟件過程越是“自動化”越好。希望需求是被穩固下來,事先就做好,可以作為“原料”輸入到一連貫相互獨立“工序”中,通過控制開發人員在每個工序中的作為,使得產品嚴格按照預先期望的模子生產出來;希望開發者都安分守己地努力,所有的溝通和交流最好都文檔化,以分清責任減少爭論,同時以文檔為驅動,規規矩矩按部就班地做上一個流程的文檔給自己規定好的事情。嗚嗚,正在焦油坑里掙扎的猛犸象還少么?

3、要面向代碼
再強調一遍:源代碼就是設計!
即使在軟件工程中,這一點也是不可否認的(雖然不被承認)。很多人認為,軟件設計是指編碼之前的“概要設計”和“詳細設計”,代碼只是“實現”,只是個體力活兒。但是,代碼卻是軟件設計最真實、準確、有價值的表現。在編碼前,再好的構思也只是“預設計”,是沒有被驗證、沒有被認可、沒有被賦予現實意義的空想。“預設計”再好也并不是設計,在所謂“概要設計”和“詳細設計”階段通常不乏玲瓏的構思,但是由于輕視了代碼(而去重視那些只是臨時使用一下的設計文檔),往往能“化神奇為腐朽”,況且沒有在代碼中被驗證與整個系統的協調性的神奇構思,又有什么值得稱道的呢?
所以,我們的設計應該面向代碼,而不是輕視它。有什么好的設計構思,最好趕緊在代碼上表達出來,并很快地驗證這個構思是正確的,是與整個系統相容的,而不是把很多構思都堆積在“設計文檔”中,掩蓋了矛盾和錯誤,并在編碼的時候把它們放大。
設計面向代碼,應該從測試用例出發。比較好的方法是采用“測試驅動開發”(TDD)(源自《測試驅動開發》,Kent Beck著)的方法,直接面對需求,把需求用測試用例準確地表達出來,所有的設計都從測試用例出發,這樣我們的設計就能夠更貼近真實的需求,直接將需求和代碼聯系起來,減少了中間所有的不確定的環節。明確的需求都是可以測試的,編寫測試用例的過程其實是一個促使開發者對需求真正認識的過程,這也是尋找好的軟件設計的出發點,實在不應該忽略或輕視。并且有了準確的測試用例做保障,我們在改進過程中隨時都可以驗證所做的修改的正確性。
設計面向代碼,要求在變化中保持代碼“健康”。代碼是在開發的過程中不停地被改變的,但是隨時要保持代碼的“健康”。這種改變不是往舊墻上刷新粉,整個過程也不是“code and fix”,特別是軟件開發的后期,最好不要有顯眼的“補丁”,而應該針對任何變化,最好把所做的修改或加強融入到現有的設計中,使之和諧不留痕跡。至于什么樣的代碼才是“健康”的,我想這是一個值得展開討論的話題。
設計面向代碼,切忌盲目復用。軟件開發為了提高資源利用率和開發效率,希望能夠在新的應用中運用已有的設計成果,最大限度地實現軟件復用,這種愿望可以理解,但復用一定是有條件有限度的。復用別人現成的代碼,天然受到別人設計的制約,要想復用,首先要充分了解別人的設計,特別考察它的“通用性”,由于不是為我們的應用定制的,所以我們的設計要遷就這種“通用性”,這種遷就是相當有風險的,很可能使我們的設計變得僵硬。不記得哪位高人曾經說過“復用只是神話”,我覺得是很有道理的,采用別人的代碼的當時是省心省力,但是后期的維護似乎還有漫長的痛苦等著你。再者,應用是有差異的,哪怕相同的應用,削足適履而不是根據腳的尺寸去做鞋,是不可取的。

4、要重構
不知道有沒有特別厲害的軟件設計高手,可以將好的設計一步到位地呈現出來?不過我想對于大多數人來說,設計并不是一蹴而就,而是循序漸進的。最初的設計可能與最終的設計相差巨大,這是再正常不過的事情。重要的是軟件設計要朝一個好的方向演進,重構的過程就是演進的過程。重構是對現有代碼的設計進行優化和改良,《重構:改善既有代碼的設計》(候捷等譯,中國電力出版社出版)一書指出了存在于代碼中的常見的22種“壞味道”,并針對性地提供了一些很中肯實用的重構手法,很值得借鑒。其實重構應該是軟件開發者的一種自覺習慣,重構手法也是經過長期實踐經驗已經潛移默化了的。
最初的設計總是丑陋的,重構是使“丑小鴨”變“白天鵝”的必要手段。哪怕再巧妙的構思,也會有制肘,哪怕再謹慎的防備,也會有疏漏,所以設計的雛形總會有這樣那樣的不足,或多或少地脫離現實問題。這些不足的地方都是可以改進的,關鍵是有意識地去尋找出這些不足,并糾正它們。因此,我們可以為了更緊密地貼合需求,放心地讓我們最初的設計粗糙一些,我們總可以用重構的方法改造它。
好的設計是不斷地重構才顯露出來的。設計應當是從需求出發,做出一些“粗糙”的解決問題的方案,然后根據經驗對該方案不斷進行改進、完善,使其越來越靈活,越來越趨于穩定,得到的,自然就是好的設計。而不是從經驗出發,先想象出一些“玲瓏”的構思,然后修修補補,使其符合我們的需求。前一種方式是借鑒經驗,后一種方式是強求經驗;前一種方式是慢慢演化水到渠成,后一種方式是急于求成生拉硬拽;前一種方式靈活,有更進一步優化的空間,后一種方式呆板,越修補越走樣。優劣自現。
設計過程中要“兩頂帽子”輪流戴。“兩頂帽子”是Kent Beck為了形象地描述重構過程中的兩項任務而引入一種比方。一頂帽子是改善現有設計,另一定帽子是增加新功能。提醒我們,任何時候應該只做一件事(除非你喜歡把兩頂帽子摞起來戴):改善現有設計的時候,就立足于現有設計,用現有的測試用例可以很方便地驗證改善的正確性;在增加新功能的時候(容納一種新的需求),就充分信任現有代碼的正確性,而立足于該功能,關注在新增功能的測試用例上以保證測試用例的正確,同時修改或增加代碼使得所有測試用例(包括新增的)通過。如果覺得新增了功能設計被污染了,別急,測試用例通過后可以換戴另一頂帽子。

5、要新陳代謝
軟件設計不是一勞永逸的,技術更新很快,軟件設計要為應用服務,就要適應新的技術條件,新陳代謝。軟件設計本身要有開放、靈活的架構,而開發者也要有與時俱進、開拓創新的精神。
軟件設計要吸納需求的變化,不斷適應新的環境。軟件是服務于應用的,應用的背景發生了變化,軟件必然要跟著變化,所以好的軟件設計要有在變化中存活和發展的生命力,這就要求軟件本身的架構是開放、靈活的。軟件架構是在軟件在開發的過程中,不斷納入新的需求,軟件設計自身不斷地進行調整,到最后穩定下來的部分。所以軟件架構是在需求不斷變化的打磨下進化而來的,是軟件設計的骨架。只有來自于實際應用的動態的軟件架構,才方便改變自己以適應應用環境的改變。
開發者要不斷學習,提升自己的眼界。軟件應用的領域范圍廣泛,軟件技術的發展也朝不同的方向在不斷地提升;軟件開發的本質規律還沒有完全大白于世,軟件業界存在著眾多的思想流派,不同流派正對一些技術方法會有一些爭議。而開發者往往只能集中精力于某一特定的方向,而對其他方向的發展比較生疏;或者開發者只信奉一個思想流派,對其他不同的意見不屑一顧——這些都是眼界受局限的表現。開發者應當了解其他領域的一些基本情況,其他流派的一些基本主張,這有益于反思自己開發中的一些困惑,啟發自己的思路。
開發者還要保持開放的心態,要革故鼎新。好的軟件設計,要經受住時代變遷的考驗和用戶的挑剔。所以設計者要有開放的心態,虛心接受用戶的批評和建議,并積極改進軟件的設計以滿足用戶的要求。特別是對于新的有用的技術元素(如新的軟件架構理念、新的網絡技術、新的數據庫技術、新興的編程語言等),要有一定的敏感度,要從中吸取積極的啟發,來加強軟件設計的技術跟進。對于一些不合時宜的設計,要更新換代,始終保持軟件設計與時代同步。
開發者還要有一點完美主義情節。軟件設計是軟件開發者心血的結晶,是開發者思想的表白,所以開發者往往對自己成功的作品愛不釋手。就像母親總覺得自己的孩子最棒一樣,開發者會覺得自己的設計是最好的。應該對那些傾心盡力做出軟件設計的開發者致以崇高的敬意!但是也有必要提醒他們,要理性客觀地對待自己作品的不足,還有不完美的地方。這些話,特別需要善意地對那些對現在的“設計”自我感覺良好的人說。相當多的開發者,長期在設計的荒原,已經習慣了在蕭瑟的秋風和飛砂走石中緝拿隱藏在荊棘中的bug,還從來沒有想象過軟件設計其實可以做得更好,沒奢望過還有鳥語花香的春天。該醒醒了,不要安于在黑暗的屋子里窒息的命運!  


段先德 2011-02-22 20:28:02

[新一篇] 難倒游戲開發的八道關

[舊一篇] 高手談做程序員的基本原則
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表