目前軟體所採用的體繫結構相對要解決的問題來說,不管要解決的問題本身是簡單還是複雜,軟體給出的解決方案通常都不會簡單,尤其是一些企業級解決方案。
軟體是用來解決問題的,如果問題域本身已經很複雜,再使用一個更加複雜的工具去解決它,其成功的機率為0。關於問題域的複雜性可以參閱布魯克斯那篇“沒有銀彈”的經典文章,本文關注的是軟體的複雜性。
從軟體的發展曆程看,複雜系統通常都會失敗,不過失敗後都通常會衍生出一個相對簡單而實用的系統。這些複雜系統初始的目標看上去都很完美,可以被當成萬能藥使用,因此其實現通常都會比較複雜,難以學習,難以使用。這時一些實現相同目標的相對簡單的系統就會出現,雖然這些簡單系統有一些缺陷,但其實用性很強,通常比其複雜的前輩更能得到推廣。 下面的例子充分說明了這點:
為簡化MULTICS而誕生的UNIX已被廣泛使用了30多年,再繼續用上30年應該也沒問題。 UNIX的核心思想就是保持簡潔性。(參見 Eric S. Raymond The Art of Unix Programming)
目前在Internet上廣泛使用的TCP/IP協議是從一個叫OSI (Open Systems Interconnection)的模型簡化後得到的。在OSI模型中,網路被分成了7層,而不是TCP/IP的4層。實現一個7層的協議要比4層協議困難得多,雖然也有7層的實現,不過由於其bug太多,最終被實現相對比較容易的TCP/IP所替代。
針對J2EE複雜性而出現的輕量級解決方案如Spring、Hibernate、Ruby On Rails等。
再舉一個mail的例子,現在使用的SMTP和POP3有很多設計上的缺陷,導致存在安全隱患和垃圾郵件橫行,但相比複雜的X.400協議,SMTP和POP3實現要簡單很多,而且大部分情況下是可以滿足使用者發送郵件的需求的。所以現在很少見到X.400。
軟體複雜性產生的原因大致可歸結為以下方面:
- 追求完美,想發明“銀彈”。希望系統能對要解決的問題給出一個完美而通用的方案,因此就將所有可能的情況都加入到系統實現中,使得系統變得複雜,龐大。如MULTICS、CORBA、OSI等, 從實踐看,這樣的系統通常不會有很長的生命力或得到廣泛應用。
- 系統使用過程中逐步變得複雜。 系統初始設計時吸取前車經驗,會比較簡單,但隨著使用者的增多,新的需求也不斷提出,另外還要在競爭中保持優先,新特性會不斷加入系統,使系統變得複雜。Java就是個例子,剛誕生時對C++作了很多簡化,很快風靡軟體界。 但到了10年後再看Java,當初簡化掉的特性基本都已加入系統。 Java的這種複雜性使得一些相對簡單的動態語言紅極一時。
- 利益驅使或個人表現。 不把系統搞得複雜一點不能夠將價格抬上去,另外將系統搞複雜一點可以讓人不能一下看出其深淺長短,以顯得高深莫測。
軟體複雜性帶來的危害:
- 增加開發難度,降低開發效率。 開發一個複雜的系統需要耗費不少精力去應對複雜性本身所帶來的附加工作量,不能有效關注在解決問題本身,造成開發效率的降低。
- 增加維護難度。 維護一個複雜系統是一個很艱巨的任務,你需要時刻防備系統不穩定性造成的故障。當要擴充系統時,其複雜性將成為一個需要花大力克服障礙。
- 增加學習使用難度。複雜性會使學習曲線變得陡峭,如果封裝得不好,會對操作帶來很多的不便,通常需要花費很多的時間才能掌握其操作。
圖靈獎得主、QuickSort演算法的發明人霍爾曾說過,開發軟體有兩種方法:一種是使系統儘可能簡單,使它看上去沒有什麼缺陷;另一種是將系統弄得很複雜,使你很難看出它有什麼缺陷。