如何降低軟體的複雜性?

阮一峰發表於2018-09-10

John Ousterhout 是史丹佛大學計算機系教授,也是 Tcl 語言的創造者。

今年四月,他出版了一本新書《軟體設計的哲學》(A Philosophy of Software Design)。這是課程講稿,160多頁,亞馬遜全部是五星好評。

我還沒讀這本書,但是我看了作者在谷歌的一次演講(Youtube),介紹了這本書的主要內容。我覺得非常值得看,大部分書教你怎麼寫正確的程式碼,這本書教你如何正確設計軟體。

下面我就根據演講視訊和網上的書評,做一下筆記。

一、什麼是複雜性

Ousterhout 教授認為,軟體設計的最大目標,就是降低複雜性(complexity)。 所謂複雜性,就是任何使得軟體難於理解和修改的因素。

Complexity is anything that makes software hard to understand or to modify.

複雜性的來源主要有兩個:程式碼的含義模糊和互相依賴。

Complexity is caused by obscurity and dependencies.

模糊指的是,程式碼裡面的重要資訊,看不出來。依賴指的是,某個模組的程式碼,不結合其他模組,就會無法理解。

Obscurity is when important information is not obvious.

Dependency is when code can't be understood in isolation.

複雜性的危害在於,它會遞增。你做錯了一個決定,導致後面的程式碼都基於前面的錯誤實現,整個軟體變得越來越複雜。"我們先把產品做出來,後面再改進",這根本做不到。

Complexity is incremental, the result of thousands of choices. Which makes it hard to prevent and even harder to fix.

二、複雜性的隔離

降低複雜性的基本方法,就是把複雜性隔離。"如果能把複雜性隔離在一個模組,不與其他模組互動,就達到了消除複雜性的目的。"

Isolating complexity in places that are rarely interacted with is roughly equivalent to eliminating complexity.

改變軟體設計的時候,修改的程式碼越少,軟體的複雜性越低。

Reduce the amount of code that is affected by each design decision, so design changes don't require very many code modifications.

複雜性儘量封裝在模組裡面,不要暴露出來。如果多個模組耦合,那就把這些模組合併成一個。

When a design decision is used across multiple modules, coupling them together.

三、介面和實現

模組分成介面和實現。介面要簡單,實現可以複雜。

Modules are interface and implementation. The best modules are where interface is much simpler than implementation.

It's more important for a module to have a simple interface than a simple implementation.

好的 class 應該是"小介面,大功能",糟糕的 class 是"大介面,小功能"。好的設計是,大量的功能隱藏在簡單介面之下,對使用者不可見,使用者感覺不到這是一個複雜的 class。

最好的例子就是 Unix 的檔案讀寫介面,只暴露了5個方法,就囊括了所有的讀寫行為。

四、減少拋錯

有些軟體設計者喜歡拋錯,一遇到問題,就丟擲一個 Exception。這也導致了複雜性,使用者必須面對所有的 Exception。"反正我告訴你出錯了,怎麼解決是你的事。"

正確的做法是,除了那些必須告訴使用者的錯誤,其他錯誤儘量在軟體內部處理掉,不要丟擲。

Tcl 語言的最初設計是,unset() 方法用來刪除已經存在的變數,如果變數不存在,該方法拋錯。Ousterhout 教授說,這個設計是一個錯誤,完全不應該拋錯,只要把 unset() 定義成讓一個變數不存在,就解決問題了。

另一個例子是,Windows 系統不能刪除已經開啟的檔案,會有錯誤提醒。這也是一個設計錯誤,有些使用者實在刪不掉這些檔案,不得不重啟系統。Unix 的做法是,總是允許使用者刪除檔案,但是不清理記憶體,已經開啟的檔案在記憶體裡面繼續存在,因此不會干擾其他程式的執行,那些程式退出儲存檔案的時候,發現檔案不存在才會報錯。這個設計比較好。

(完)

相關文章