寫程式是人類歷史上最純粹的創造活動之一。程式設計師不像建築師或工程師那樣受到物理定律的束縛,只要能想像出一個系統,幾乎都能用程式碼把它實作出來。換句話說,寫軟體最大的限制不在硬體,而在於我們能否理解自己正在打造的系統。
軟體開發的真正瓶頸#
當程式逐步演進、加入更多功能,元件之間的依賴關係會變得愈來愈微妙:
- 複雜性會隨時間累積,使開發者愈來愈難在腦中同時掌握所有相關因素
- 累積的複雜性會拖慢開發速度、製造 bug,bug 又進一步拖慢開發、推高成本
- 程式規模愈大、參與人員愈多,複雜性的管理就愈困難
工具固然能幫忙,但工具不是萬靈丹。要讓更大、更強的系統得以被建造出來,我們必須讓軟體本身變得更簡單。
對抗複雜性的兩種途徑#
本書討論兩條對抗複雜性的路線:
- 消除複雜性:把程式碼寫得更簡單、更顯而易見。例如消除特殊情況(special cases)、以一致的方式使用識別字
- 封裝複雜性:透過模組化設計(modular design)把系統切成相對獨立的模組(例如物件導向語言中的類別),讓程式設計師在處理某個模組時,不必同時揹負其他模組的細節
軟體設計是持續性的活動#
軟體與橋樑、船舶不同,它是高度可塑(malleable)的:
- 大型軟體系統的初始設計幾乎不可能事先完全想清楚
- **瀑布模型(waterfall model)**把設計凍結在開頭階段,因此遇到後期才浮現的設計問題時,往往只能繞著補丁打轉,導致複雜性爆炸
- **敏捷開發(agile development)**等增量式(incremental)做法則先從一小部分功能著手,邊做邊修正,新加入的功能能受惠於先前累積的經驗
增量式開發意味著「軟體設計永遠沒有完成的一天」。
設計貫穿系統整個生命週期;既然設計從未停止,思考複雜性也就從未停止。
本書的兩個目標#
- 描述複雜性的本質:什麼是複雜性?為什麼它重要?怎麼辨識程式中不必要的複雜性?
- 提供降低複雜性的技術:作者並不打算給出一份保證寫出好設計的食譜,而是提出一組偏哲學性的高層次原則(例如「類別要深」、「將錯誤定義到不存在」),用來比較設計選項、引導探索
如何使用本書#
本書中的設計原則相當抽象,不容易在脫離程式碼的情況下體會。建議的使用方式:
- 配合程式碼審查(code review)一起閱讀:別人的程式碼比自己的更容易看出設計問題,可用書中的紅旗(red flags)找出問題並提出改進
- 學會辨識紅旗:紅旗是「這段程式碼可能比實際需要更複雜」的訊號,書末會彙整最重要的紅旗清單;遇到紅旗時,停下來嘗試改寫
- 多嘗試替代方案:第一個能讓紅旗消失的設計往往不是最好的,多試幾種方案是學習設計最快的方法
任何設計原則一旦推到極端,幾乎都會變糟。
好的設計反映的是各種競爭性概念之間的平衡。本書多個章節都有「過猶不及」一節(Taking it too far),說明何時是「好東西用過頭」。
範例與適用範圍#
- 書中範例多以 Java 與 C++ 撰寫,討論也多圍繞物件導向語言的類別設計
- 但這些觀念同樣適用於:
- 沒有物件導向特性的語言(如 C)中的函式
- 類別以外的模組形式(subsystem、網路服務等)