《Refactoring 重構》Podcast 準備稿:重構的原則・程式碼壞味道
書名: 重構 Refactoring(Improving the Design of Existing Code, 2nd Edition) 作者: Martin Fowler 系列: 啃一本大書(恩普拉氏) 公開標題建議: 啃一本大書|Refactoring.兩頂帽子與壞味道診斷學 (2/8) 涵蓋範圍: 第2章 Principles in Refactoring(重構原則)+ 第3章 Bad Smells in Code(程式碼壞味道)
背景速覽
EP1 用範例讓你「看見」重構,這集回過頭把理論骨架補上:什麼是重構(定義)、為什麼要做(效益)、什麼時候做(時機),以及團隊導入時的挑戰。接著進入全書最常被引用的一章——「程式碼壞味道(Code Smells)」,這章是重構的診斷學:教你怎麼「聞」出一段 code 哪裡不對勁,並對應到該用哪招重構。這兩章合起來,就是重構的「為什麼」加「從哪裡下手」。
一句話重點
重構的紀律有兩條核心:「重構帽」和「功能帽」絕不同時戴(一次只做一件事);而要知道何時該重構,就靠培養對「壞味道」的嗅覺——壞味道不是 bug,是設計問題的徵兆,是「這裡未來會痛」的早期警報。
值得討論的重點
1. 重構的定義:名詞與動詞,核心都是「不改變外在行為」
Fowler 把「重構」拆成兩層:當名詞,它是「不改變軟體可觀察行為的前提下,調整內部結構以提高可理解性、降低修改成本」的一個動作;當動詞,它是「用一連串這樣的動作重新架構軟體」。關鍵詞永遠是那句——不改變外在行為。這條界線是重構安全性的全部基礎:因為行為不變,你才能用測試驗證「我沒弄壞東西」。
2. 兩頂帽子:重構與加功能,分開做
這是整本書最該被記住的一句話。寫程式時你其實在做兩種截然不同的事:加功能(改變行為、增加能力,不動既有結構)和重構(改變結構、不動行為)。Fowler 要求你時刻清楚自己現在戴哪頂帽子,而且不要同時戴兩頂。為什麼?因為一旦混做,出了錯你分不清是「新功能寫錯」還是「重構弄壞了」,測試也救不了你。心理上常常一天要換好幾次帽子,但每一刻只戴一頂。
3. 何時重構:不是專案末期的大掃除,而是隨手、隨時
Fowler 反對「先寫爛、最後再排一個重構衝刺」的做法。他列出幾種更健康的時機:
- 三次法則(Rule of Three):同樣的東西出現第三次,就抽象化。第一次照做,第二次皺眉照做,第三次就重構。
- 預備性重構(Preparatory):要加功能前,先把結構整理到「加起來很順」——「我要往東,但這裡得先往西繞一下整地」。
- 理解性重構(Comprehension):看不懂舊 code 時,邊讀邊重構,把你讀懂的理解「寫進」結構裡。
- 撿垃圾式重構(Litter-Pickup):路過看到髒,順手清一點(對應《Clean Code》的童子軍規則)。
4. 壞味道診斷學:聞到味道 → 對應手法
第3章是一本「症狀對照表」。重點不是背完所有味道,而是建立反射:看到某種症狀,腦中自動跳出對應的重構。幾個最該講的:
- Mysterious Name(神秘命名):取不出好名字,往往代表你自己還沒想清楚這東西是什麼——命名困難是設計沒想清的訊號。對應 Rename。
- Duplicated Code(重複):對應 Extract Function / 搬移。
- Long Function(過長函式) 與 Long Parameter List(過長參數列):Fowler 是「短函式」的強力擁護者,對應 Extract Function、Introduce Parameter Object。
- Divergent Change vs Shotgun Surgery:這對最容易搞混——發散式修改是「一個類別因多種不同原因被改」(一個類別承擔太多變化軸);散彈式修改是「一種變化要改動好多個類別」(變化軸被打散了)。一個要拆開,一個要聚攏。
- Feature Envy(依戀情結):一個函式對別人家的資料比對自己家更有興趣 → 把函式搬去資料所在的類別。
- Primitive Obsession(基本型別偏執):用 string/number 硬撐業務概念(電話號碼、金額) → 換成 Value Object。
注意事項
⚠️ 壞味道不是道德審判,是風險管理。 聞到味道不代表你「必須」立刻改——它只是一個訊號,告訴你「這裡未來可能會痛、可能是維護熱點」。要不要動,看這塊 code 你接下來會不會碰、改動風險多高。別讓聽眾變成強迫症,看到任何 smell 都要當場清掉。
⚠️ 「兩頂帽子」聽起來簡單,做起來最常被破壞。 真實情境是:你重構到一半,順手「啊這裡 bug 我順便修一下」——這就是同時戴兩頂帽子。提醒聽眾:發現 bug 就記下來,重構告一段落、跑過測試後,換帽子再去修。
⚠️ Fowler 對「Comments(註解)」這個味道的態度容易被誤讀。他不是反對所有註解,而是說:很多註解是「除臭劑」——拿來掩蓋爛 code 的味道。能用好命名、好結構讓 code 自我說明的,就別靠註解。但解釋「為什麼這樣做」(why) 的註解仍然有價值。
專家補充
💡 「設計耐力假說(Design Stamina Hypothesis)」是這章的隱形主角(書裡兩張圖)。Fowler 主張:好設計的累積功能產出,一開始可能比爛設計慢,但會在某個交叉點之後反超,而且差距越拉越大。這條曲線是「為什麼要花時間重構」最有力的論證——重構不是成本,是讓你未來跑得快的投資。可以對照《Clean Code》EP1 那條「生產力 vs 時間」曲線,兩本講的是同一件事。
💡 「Code Smell」這個詞其實是 Kent Beck 發明的(Fowler 在書裡致謝)。這個比喻之所以好,是因為它精準捕捉了一種狀態:你說不太出哪裡錯,但就是覺得不對勁。把這種模糊的工程直覺命名出來、還配上對照表,是這本書最大的貢獻之一——它把「資深工程師的嗅覺」變成可教、可查的東西。
💡 Divergent Change / Shotgun Surgery 這對其實是 SOLID 裡「單一職責原則(SRP)」的兩種違反方式。Uncle Bob 的 SRP 名言「一個模組只該有一個改變的理由」,正好同時解釋了這兩個味道:發散式修改=理由太多了;散彈式修改=一個理由散落在太多模組。兩本經典在這裡握手。
討論問題
🎙️ 開場鉤子:「你有沒有過——重構改一改,順手又修了個 bug,結果測試紅了,你完全不知道是重構弄壞的還是 bug 沒修對?今天第一個重點就在救你這種時刻。」
🎙️ 自問自答:「重構到底值不值得花時間?老闆會問。」——用設計耐力假說回答:短期看是繞路,但好設計的交叉點之後你會越跑越快,而且現實中團隊幾乎都是重構「太少」而非「太多」。
🎙️ 帶走的一題:「下次你寫 code 之前,先問自己一句:我現在戴的是哪頂帽子?加功能,還是重構?光是養成這個自問,你的 commit 就會變乾淨。」
更大範圍關聯
- 與《Clean Code》互文:壞味道章和《Clean Code》最後一章「氣味與啟發(Smells and Heuristics)」幾乎是同一個傳統——Uncle Bob 那章很大程度受 Fowler 啟發。兩本一起讀,診斷學會更完整。
- 與 SOLID:壞味道是「症狀」,SOLID 是「原則」。聞到 Shotgun Surgery,背後其實是違反了 SRP——味道幫你發現問題,原則幫你理解為什麼。
- 與《重構 Legacy Code》的分工:Fowler 的時機論(預備性、理解性重構)假設你已經有測試。當你面對「連測試都沒有的老 code」,那是 Michael Feathers《Working Effectively with Legacy Code》的主場——它教你怎麼在沒有安全網的地方先架出安全網(見 [軟體工程領域切分藍圖])。
- 思想座標:「演進式設計」需要重構作為引擎,而重構需要壞味道作為導航——這三者構成敏捷時代「不靠大設計先行也能維持好設計」的方法論基礎。
錄製建議
- 建議時長:約 20-22 分鐘。配比:重構定義+兩頂帽子約 6 分、何時重構(四種時機)約 5 分、壞味道診斷學約 9 分、收束約 2 分。
- 討論策略:前半(原則)用「兩頂帽子」當主軸最好記、最好用,把定義和時機都掛在這條線下。後半(壞味道)別逐一念表——挑 5-6 個最有畫面的味道,每個用「症狀 → 為什麼是問題 → 對應哪招」三句話講完,並反覆強調「聞到味道是訊號,不是命令」。
- 這集理論密度高,多用聽眾自己的經驗(接手爛 code、改一處壞一片)來接地氣,避免變成定義朗讀。