簡介

為什麼程式碼越寫越難改?為什麼一個小需求會牽連十幾個檔案?為什麼每個類都很短,整個系統卻沒人敢動?
斯坦福大學教授、Tcl 語言的作者 John Ousterhout 用一本篇幅不長的書回答了這些問題:軟體設計的核心任務,是控制複雜性。全書圍繞這一把尺子,討論每個程式設計師每天都要面對的決定:模組該切多大,介面怎樣才算好,註解該寫什麼,名字怎麼取,例外怎樣處理,什麼時候值得多花一點時間把設計做對。它提供的是一套判斷方式。讀完之後再看自己的程式碼,感覺會不一樣。
這個版本有什麼不同
這個版本在社群譯稿的基礎上,對照第二版英文原文逐段修訂,也為中文讀者補充了圖解與程式碼對照。
- 譯文重新修訂。 理順生硬句式的同時,逐句核對條件、否定、因果與適用範圍,保留作者的論證與分歧。核心術語全書統一,並區分容易混淆的含義:例如“深模組”的“深”與軟體層級無關,“複雜性”也不等於演算法的時間複雜度。裁定過的譯法見術語表。
- 每章開頭一張概覽,結尾一張回顧。 概覽回答“這章講什麼、幾塊內容怎麼關聯”,回顧用幾幕故事重講這章想強調什麼,都可以收起。
- 50 幅譯者圖解穿插正文。 用模組邊界、狀態變化和呼叫關係,把抽象原則畫出來:為什麼這個介面更深,為什麼那個特殊情況可以消失,一次內部修改會波及多遠。
- 原書插圖的向量重繪。 12 幅原書低解析度插圖都有可放大的 SVG 版本,也可以一鍵切回原圖對照。
- 60 段程式碼範例的 Python 對照。 原書範例以 Java、C 與 C++ 為主。每段範例都提供可切換的 Python 教學改寫,保留原例中的設計反例,並註明語言語義上的差異,方便把注意力放在設計問題本身。
- 為長時間閱讀設計的閱讀器。 簡繁切換、字號調整、明暗主題、全書搜尋、章節目錄、本章小節定位、閱讀進度,以及腳註與正文之間的往返跳轉。偏好和上次讀到的章節儲存在瀏覽器裡。
- 補充內容與原書正文分開呈現。 圖解、概覽、回顧與 Python 改寫都標註為譯者補充;原書本身的疑點保留原文,另以譯者注說明勘誤依據。
閱讀器一覽
| 位置 | 功能 |
|---|---|
| 頂欄 | 原例 / Python 切換,簡體 / 繁體切換,閱讀設定(字號、淺色或深色、原圖是否用 SVG 顯示),本章目錄 |
| 左欄 | 全書搜尋,章節列表 |
| 右欄 | 本章目錄,隨滾動高亮當前小節 |
| 章首 | 本章概覽,預設展開,可收起、放大、下載 SVG |
| 正文 | 譯者圖解內嵌顯示;程式碼範例可逐段或全書切換為 Python;原書插圖可在原圖與 SVG 重繪之間切換;腳註點選往返 |
| 章末 | 本章回顧,上一章 / 下一章 |
| 簡介頁 | 顯示上次讀到的章節,一鍵繼續閱讀 |
目錄
| 章節 | 內容概覽 |
|---|---|
| 前言 | 我們學過語言、演算法和工具,卻很少專門學習如何設計軟體。這本書關心的,正是怎樣把複雜問題拆成可以獨立理解和解決的部分。 |
| 第 1 章 介紹 | 軟體總在變化,設計也不能止於開工前的一張藍圖。貫穿每次迭代的任務,是不讓複雜性隨著功能一起失控地增長。 |
| 第 2 章 複雜性的本質 | 複雜性常從小處顯現:一處改動牽連許多地方,動手前要記住太多細節,甚至不知道自己遺漏了什麼。依賴性與模糊性就這樣慢慢積累。 |
| 第 3 章 能工作的程式碼是不夠的 | 只求程式碼儘快能跑,往往會把這些負擔留給未來。作者主張換一種節奏:每次多花一點心思改善設計,為後續開發省下力氣。 |
| 第 4 章 模組應該是深的 | 這種投入首先體現在模組上。好的模組用簡單的介面提供豐富的功能,讓使用者不必深入內部,也能把事情做好。 |
| 第 5 章 資訊隱藏和資訊洩露 | 要做到這一點,關鍵不是把程式碼分散到更多類裡,而是把設計決策藏在恰當的地方,避免一次內部變化迫使其他模組跟著修改。 |
| 第 6 章 通用的模組是更深的 | 當介面不再緊貼某個具體場景,而是提供少量通用操作時,模組往往能隱藏更多細節,許多特殊情況也隨之消失。 |
| 第 7 章 不同的層級,不同的抽象 | 模組組成層級後,每一層還應帶來新的抽象。若只是把參數和呼叫原樣轉交,增加的層次可能只會讓人多繞一圈。 |
| 第 8 章 下沉複雜性 | 真正有用的封裝,有時需要實作者多承擔一些麻煩:在模組內部處理與自身功能相關的複雜性,換取眾多呼叫者的簡單。 |
| 第 9 章 在一起更好還是分開更好? | 由此,拆分還是合併就不能只憑程式碼長短決定。需要共享知識的程式碼可以放在一起,通用與專用的職責則應分清,最終看整個系統是否更容易理解。 |
| 第 10 章 透過定義來規避錯誤 | 例外也要接受同樣的審視。有些錯誤條件可以透過重新定義操作來消除;其餘例外則可考慮遮蔽或集中處理,減少散落各處的特殊分支。 |
| 第 11 章 設計兩次 | 這些選擇很難一次想對。與其急著實作第一個方案,不如先構思幾種明顯不同的設計,再比較哪一種介面更簡單、依賴更少。 |
| 第 12 章 不寫註解的四個藉口 | 但再好的結構,也無法讓程式碼說出所有重要資訊。書的目光因此轉向註解:它不是設計失敗後的補救,而是幫助讀者理解抽象的一部分。 |
| 第 13 章 註解應該描述從程式碼中不易看出的資訊 | 有價值的註解不會逐句複述程式碼,而會補上程式碼沒有說清的內容:邊界在哪裡,哪些規則必須遵守,以及為什麼這樣設計。 |
| 第 14 章 選取名稱 | 名稱則是更短小的說明。一個準確、一致的名字,能讓讀者形成正確的預期,省去猜測,也減少由誤解造成的缺陷。 |
| 第 15 章 先寫註解 | 文字還可以反過來檢驗設計。不妨先寫清介面的用途與約定,若怎麼都解釋不明白,就有機會在編碼之前發現抽象的問題。 |
| 第 16 章 修改現有的程式碼 | 當軟體進入持續維護階段,這種設計意識仍不能放下。每次修改既要照顧當前需求,也要維護整體結構,讓註解與實際行為一同更新。 |
| 第 17 章 一致性 | 隨著程式碼越來越多,一致的約定能讓已有經驗反覆派上用場。相似的事物採用相似做法,不同的事物也應有清楚的區別。 |
| 第 18 章 程式碼應該是易理解的 | 這些努力最終匯成同一種閱讀體驗:看到程式碼時,讀者能較快地理解它,而不是四處尋找線索。設計應減少他們需要知道的事,並把必要的資訊交代清楚。 |
| 第 19 章 軟體發展趨勢 | 有了這把尺子,面對流行的方法也就不必盲從。繼承、敏捷、測試和設計模式,都值得追問一句:它究竟是在降低複雜性,還是在增加負擔? |
| 第 20 章 效能設計 | 效能同樣不是放棄整潔設計的理由。先透過測量找到真正的瓶頸,再讓關鍵路徑少做不必要的工作,簡單與高效往往可以兼得。 |
| 第 21 章 決定什麼是重要的 | 更深一層看,這些原則都在訓練同一種判斷:什麼值得關注,什麼可以隱藏。讓重要的事情清楚可見,同時儘量減少必須操心的事情。 |
| 第 22 章 結論 | 於是,全書回到最初的主張:好的設計來自持續的小額投入。把降低複雜性變成日常習慣,既能減輕未來的維護負擔,也能讓程式設計更有樂趣。 |
| 總結 | 最後,書中把設計原則與危險訊號匯在一起。它們不是必須照辦的戒律,而是留給下一次設計與程式碼審查的一組提醒。 |
反饋
發現翻譯問題或有改進建議,歡迎到 GitHub 儲存庫 提交 Issue 或 Pull Request。譯文基於 yingang/aposd2e-zh 的社群譯稿修訂而來,感謝原譯者與所有貢獻者。原書版權歸作者所有;譯文與補充材料採用 CC BY 4.0 許可。