我常常感到幸運,我們現在所處的是一個令人振奮的時代,我們進入了軟體工業時代。在這個時代裡,我們進行軟體開發已經不再是一個一個的小作坊,我們在進行著集團化的大規模開發。我們開發的軟體不再是為某個車間、某個工序設計的協助工具輔助,它從某個單位走向整個集團,走向整個行業,甚至整個社會,發揮著越來越重要的作用。一套軟體所起到的作用與影響有多大,已經遠遠超越了所有人的想象,成為一個地區、一個社會,乃至整個國家不可或缺的組成部分。慢慢地,人們已經難以想象沒有某某軟體或系統的生活和工作會是怎樣的。這就是軟體工業時代的重要時代特徵。
然而,在這個令人振奮的軟體工業時代,處於時代中心的各大軟體企業卻令人沮喪。軟體規模越來越龐大,軟體結構越來越複雜的同時,卻是軟體品質越來越低下,軟體維護變得越來越困難,以至於每個小小的變更都變得需要傷筋動骨。研發人員為此舉足無措,測試人員成為唯一的救星,每個小小的變更都需要付出巨大代價進行測試。軟體企業在這樣一種惡性迴圈中苦苦支撐。毫無疑問,這也成為這個令人振奮的時代的另一個特徵。
是的,面對軟體工業時代我們並沒有做好準備。過去,一套軟體的生命週期不過2~3年時間,隨著軟體需求的變化,我們總是選擇將軟體推倒了重新開發,但是現在這樣的情況在發生著改變。隨著軟體規模的擴大,軟體資料的積累,軟體影響力的提升,我們,以及我們的客戶,都真切地感受到,要推倒一套軟體重新開發,將變得越來越困難而不切實際。這樣的結果就是,我們的軟體將不停地修改、維護、再修改、再維護……直到永遠。這是一件多麼痛苦的事情啊!
一套軟體,當它第一次被開發出來的時候,一切都十分清晰:清晰的業務需求、清晰的設計思路、清晰的程式碼。但經曆了幾次需求變更與維護以後,一切就變得不那麼清晰了。業務需求文檔變得模糊不清,設計思路已經跟不上變更的腳步,程式碼則隨著商務邏輯的複雜而臃腫不堪。程式員開始讀不懂代碼,軟體開發工作變得不再是一種樂趣。
隨著時間的推移,軟體經過數年、數十次的變更與維護,情況變得越來越糟。最初的程式員已經不願再看到自己的代碼而選擇離去。他的繼任者們變得更無所是從,由於看不懂程式,代碼的每一次修改如同在走鋼絲。測試人員變成了唯一的希望,開發人員的每一次修改都意味著測試人員需要把所有程式測試一遍。繼任者們開始質問最初的設計者們,程式是怎麼設計的。如果此時恰巧又有什麼新技術出現,就會更顯得原有系統的破舊與不堪。
相信這就是軟體工業時代的所有企業都不得不面對的尷尬境地。難倒真的是我們最初的設計錯了嗎?是的,我們都這樣質問過我們自己,因此我們開始嘗試在軟體設計之初投入更多的精力。我們開始投入更多的時間作需求調研,考慮更多可能的需求變化,做更多的介面,實現更加靈活但複雜的設計。然後呢,我們解決了我們的問題了嗎?顯然是沒有。需求並沒有像我們想象的那樣發生變更:我們之前認為可能發生的變更並沒有發生,使我們為之做出的設計變成了擺設;我們之前沒有考慮到的變更發生了,讓我們猝不及防,軟體品質開始下降,我們被打回了原形。難倒真的是無藥可解了嗎?在我看來,如果我們沒有看明白軟體開發的規律與特點,那麼我們永遠找不到那份嚮往已久的解藥。現在,讓我們真正靜下心來分析分析軟體開發的規律與特點吧。
本欄目更多精彩內容:http://www.bianceng.cn/Programming/project/
軟體,特別是管理軟體,其實質是對真實世界的類比。我們通過對真實世界的類比,實現電腦的資訊化管理,來提高我們的生產效率。然而,真實的世界複雜而多變的,我們認識世界卻是一個由簡單到複雜循序漸進的過程,這是一個我們無法改變的客觀規律。因此,毫無疑問,遵循著這樣一個客觀規律,我們的軟體開發過程必然也是一個由簡單到複雜循序漸進的過程。
最初,我們開發的是一個對真實世界最簡單、最主要、最核心部分的類比。因為簡單,我們的思路變得清晰而明了。但是,我們的軟體不能永遠只是類比那些最簡單、最主要、最核心的部分。我們的客戶在使用軟體的過程中,如果遇到那些不那麼簡單、不那麼主要、不那麼核心的情況時,我們的軟體就無法處理了,這是客戶無論如何不能接受的。因此,但軟體的第一個版本交付客戶以後,客戶的需求就開始變更。
客戶的需求永遠不會脫離真實世界,也就是說,真實世界不存在的事物、現象、關係永遠都不可能出現在軟體需求中。但是,真實世界的事物、規則與聯絡並不是那麼的簡單與清晰的。隨著我們的軟體對它類比得越來越細緻,程式的商務邏輯開始變得不再那麼清晰而易於理解,這就是軟體品質下降最關鍵的內因。
任何一個軟體的設計,總是與軟體的複雜度有密切的關係。舉例來說吧,客戶資料是許多系統都必須要記錄的重要訊息。起初,我們程式簡單,客戶資料只記錄了一些簡單的資訊,如客戶名稱、地址、電話等等,但隨著程式複雜度的增加,客戶資料開始變得複雜。比如,起初“地址”欄位就僅僅需要一個字串就可以了,但隨著需求的變更,它開始有了省份、城市、地區、街道等資訊。隨後還會有郵遞區號、所屬社區、派出所等資訊。起初增加一個兩個欄位時我們還可以在“客戶資訊表”裡湊合一下,但後來我們必須要及時調整我們的設計,將地址提取出來單獨形成一個“地址資訊表”。如果不及時予以調整,“客戶資訊表”將越來越臃腫,由10來個欄位,變成50個、80個、上100個……
資訊表尚且如此,業務操作更是如此。起初的業務操作是如此的簡單而明了,以至於我們不需要花費太多的類就可以將它們描述清楚。比如開票操作,最初的需求就是將已開具的票據資訊讀取出來,儲存,並統計出本月開票量及金額。這樣一個簡單操作,設計成一個簡單的“開票業務類”合情合理。但隨後的商務邏輯變得越來越複雜,我們要檢查客戶是否存在、開票人是否有許可權、票據是否還有庫存,等等。起初的開票方式只有一種,但隨著非正常開票的加入,開票方式不再單一,而統計方式也隨之變化……隨著業務的不斷增加,軟體代碼的規模也在發生著質的變化。如果這時我們不及時調整我們的設計,而是將所有的程式都硬塞進“開票業務類”,那麼程式品質必然會退化。“開票業務類”由原有的數十行,激增到數百行,甚至上千行。這時的代碼將難於閱讀,維護它將變成一種痛苦,毫無樂趣可言。
面對這樣的狀況,我們應當怎樣走出困境呢?毫無疑問,就是重構,軟體的重構。開票前的校正真的屬於“開票業務類”嗎?它們是否應當被提取出來,解耦成一個一個的校正類。正常開票與非正常開票真的應該寫在一起嗎?是否我們應當把“開票業務類”抽象成介面,以及正常開票與非正常開票的實作類別。這就是我給大家的良方:當軟體因為需求變更而開始漸漸退化時,運用軟體重構改善我們的結構,使之重新適應軟體需求的變化。