寫程式是人類歷史上最純粹的創造活動之一。程式設計師不像建築師或工程師那樣受到物理定律的束縛,只要能想像出一個系統,幾乎都能用程式碼把它實作出來。換句話說,寫軟體最大的限制不在硬體,而在於我們能否理解自己正在打造的系統

軟體開發的真正瓶頸#

當程式逐步演進、加入更多功能,元件之間的依賴關係會變得愈來愈微妙:

  • 複雜性會隨時間累積,使開發者愈來愈難在腦中同時掌握所有相關因素
  • 累積的複雜性會拖慢開發速度、製造 bug,bug 又進一步拖慢開發、推高成本
  • 程式規模愈大、參與人員愈多,複雜性的管理就愈困難

工具固然能幫忙,但工具不是萬靈丹。要讓更大、更強的系統得以被建造出來,我們必須讓軟體本身變得更簡單

對抗複雜性的兩種途徑#

本書討論兩條對抗複雜性的路線:

  1. 消除複雜性:把程式碼寫得更簡單、更顯而易見。例如消除特殊情況(special cases)、以一致的方式使用識別字
  2. 封裝複雜性:透過模組化設計(modular design)把系統切成相對獨立的模組(例如物件導向語言中的類別),讓程式設計師在處理某個模組時,不必同時揹負其他模組的細節

軟體設計是持續性的活動#

軟體與橋樑、船舶不同,它是高度可塑(malleable)的:

  • 大型軟體系統的初始設計幾乎不可能事先完全想清楚
  • **瀑布模型(waterfall model)**把設計凍結在開頭階段,因此遇到後期才浮現的設計問題時,往往只能繞著補丁打轉,導致複雜性爆炸
  • **敏捷開發(agile development)**等增量式(incremental)做法則先從一小部分功能著手,邊做邊修正,新加入的功能能受惠於先前累積的經驗

增量式開發意味著「軟體設計永遠沒有完成的一天」。

設計貫穿系統整個生命週期;既然設計從未停止,思考複雜性也就從未停止

本書的兩個目標#

  1. 描述複雜性的本質:什麼是複雜性?為什麼它重要?怎麼辨識程式中不必要的複雜性?
  2. 提供降低複雜性的技術:作者並不打算給出一份保證寫出好設計的食譜,而是提出一組偏哲學性的高層次原則(例如「類別要深」、「將錯誤定義到不存在」),用來比較設計選項、引導探索

如何使用本書#

本書中的設計原則相當抽象,不容易在脫離程式碼的情況下體會。建議的使用方式:

  • 配合程式碼審查(code review)一起閱讀:別人的程式碼比自己的更容易看出設計問題,可用書中的紅旗(red flags)找出問題並提出改進
  • 學會辨識紅旗:紅旗是「這段程式碼可能比實際需要更複雜」的訊號,書末會彙整最重要的紅旗清單;遇到紅旗時,停下來嘗試改寫
  • 多嘗試替代方案:第一個能讓紅旗消失的設計往往不是最好的,多試幾種方案是學習設計最快的方法

任何設計原則一旦推到極端,幾乎都會變糟。

好的設計反映的是各種競爭性概念之間的平衡。本書多個章節都有「過猶不及」一節(Taking it too far),說明何時是「好東西用過頭」。

範例與適用範圍#

  • 書中範例多以 Java 與 C++ 撰寫,討論也多圍繞物件導向語言的類別設計
  • 但這些觀念同樣適用於:
    • 沒有物件導向特性的語言(如 C)中的函式
    • 類別以外的模組形式(subsystem、網路服務等)