《Refactoring 重構》Podcast 準備稿:簡化條件邏輯
書名: 重構 Refactoring(Improving the Design of Existing Code, 2nd Edition) 作者: Martin Fowler 系列: 啃一本大書(恩普拉氏) 公開標題建議: 啃一本大書|Refactoring.拆掉巢狀 if,用多型取代 switch (7/8) 涵蓋範圍: 第10章 Simplifying Conditional Logic(簡化條件邏輯)
背景速覽
條件邏輯是程式最容易長成一團亂麻的地方——巢狀 if、散落各處的 switch、為了處理特例而塞滿的 null 檢查。第10章 Simplifying Conditional Logic 是專治這類複雜度的工具箱:Decompose Conditional、Consolidate Conditional、Replace Nested Conditional with Guard Clauses、Replace Conditional with Polymorphism、Introduce Special Case、Introduce Assertion。這集是手法目錄裡「最解氣」的一集——每一招都能把一坨讓人頭痛的判斷邏輯,變成一眼能讀懂的結構。
一句話重點
複雜的條件邏輯之所以難讀,是因為它把「在判斷什麼」和「判斷成立後做什麼」糾纏在一起;簡化的核心動作就是把它們拆開、命名、攤平——而當條件是在「按型別分流」時,最徹底的解法是讓多型替你做這件 if-else。
值得討論的重點
1. Decompose Conditional:把條件式拆成有名字的函式
一個 if (date.before(SUMMER_START) || date.after(SUMMER_END)) 這種條件,讀者得停下來解析才知道「喔,這是在判斷『不在夏季』」。Decompose Conditional 把條件、then 分支、else 分支各自抽成命名清楚的函式(isSummer(date)、summerCharge()、regularCharge())。原本要動腦解析的判斷,變成一句能讀的話。 這是 Extract Function 用在條件邏輯上的特例。
2. Replace Nested Conditional with Guard Clauses:把異常情況提早送走
這是全章最該推廣的一招。巢狀 if(一層包一層、核心邏輯縮在最裡面)讓人得記住一路上所有條件才讀得到重點。Guard Clause(防衛敘句)的做法:把「異常/特殊情況」用提早 return 在開頭就處理掉,讓函式主體一路平鋪到底,只剩正常邏輯。
關鍵在語意:巢狀 if 把所有分支當成「地位平等的選項」;Guard Clause 則明確宣告「這幾種是異常,先打發走;剩下的才是這個函式真正在乎的事」。這個語意差異,比少幾層縮排更重要。
3. Replace Conditional with Polymorphism:用型別取代 switch
當你發現同一組 switch / if-else(「如果是經理…如果是工程師…」)在 code 裡反覆出現(EP2 的 Repeated Switches 味道),就是多型登場的時候。把每個分支變成一個子類別,覆寫同一個方法,switch 就消失成了一次多型呼叫——employee.getRole() 自己會跑到對的子類別。好處:新增一個型別只要加一個子類別,不必去翻所有 switch 補一個 case。
這招是這整章、甚至整本書最有「物件導向威力」的時刻,但也最該謹慎(見注意事項)。
4. Introduce Special Case(Null Object):讓特例不再到處檢查
當 code 裡到處是 if (customer == null) { 用預設值 } 這種重複的特例處理,Introduce Special Case 造一個「特例物件」(如 UnknownCustomer),讓它自己回傳那些預設行為。於是所有的 null 檢查消失了——你直接呼叫,特例物件會給出合理的預設。這是經典的 Null Object Pattern。
5. Introduce Assertion:把隱含假設寫出來
有些 code 默默假設「此處 discount 一定大於 0」,但這假設只存在於寫程式者腦中。Introduce Assertion 把它變成一句明確的斷言——斷言不改變程式行為,它是寫給讀者看的「溝通」,也是除錯時的哨兵。
注意事項
⚠️ Replace Conditional with Polymorphism 不是 switch 的萬靈丹——這是全書最大的爭議點之一。 把每個 switch 拆成子類別+多型,會讓「一個情境的完整邏輯」散到多個檔案;要看懂某個型別做什麼,你得開好幾個類別。當分支邏輯簡單、或本來就集中在一處時,一個直白的 switch 反而更好讀。判準是「這組分流是否反覆出現、是否會頻繁新增型別」——是,多型划算;只出現一兩次,留著 switch。提醒聽眾:別把「用多型消滅 switch」當教條,這正是《Clean Code》也被批評的同一個點。
⚠️ Guard Clause 也可能過頭。 一個函式開頭塞十個提早 return,同樣難讀。Guard Clause 適合「少數異常 + 一個主線」的情況;如果分支本來就是地位平等的多個正常選項,硬拆成一串 guard 反而失真。
⚠️ Introduce Assertion 不該拿來做「該有的錯誤處理」。 斷言是用來抓「絕不該發生(發生就是程式 bug)」的情況,不是用來驗證使用者輸入或外部資料——那些是正常的錯誤處理,要好好回應,不是 assert 了事。很多語言在 production 還會把 assert 關掉。
專家補充
💡 Guard Clause 背後是一個比「少縮排」深的觀念——它在表達「意圖的層級」。把異常先送走,等於在告訴讀者:「接下來這一大段,你不用再煩惱那些邊角情況,這裡只講主線。」好的條件結構不只是邏輯正確,它在替讀者管理注意力。這跟 EP1 那句「程式是寫給人看的」是同一個信仰。
💡 「Replace Conditional with Polymorphism」是物件導向 vs 程序式的經典戰場。OO 陣營說「多型消滅 switch,符合開閉原則」;但 John Ousterhout《A Philosophy of Software Design》和不少 FP 支持者反駁:把一個情境的邏輯打散到多個淺類別,增加的認知跳轉成本可能高於 switch。這裡沒有標準答案——重點是 Fowler 自己也說「用判斷力」。 在節目裡呈現這場辯論,比直接站隊更有價值,也呼應整季「經典要帶腦讀」的定調。
💡 Null Object Pattern(Introduce Special Case)能根除一整類 NullPointerException。更廣地看,這是「用型別/物件取代特殊值」的思路——和現代語言用 Option/Maybe/Result 取代 null 是同一個方向:與其讓每個呼叫端記得檢查特例,不如讓特例本身變成一個「行為正常」的物件或型別。Tony Hoare 稱 null 是他的「十億美元錯誤」,這招正是在還這筆債。
討論問題
🎙️ 開場鉤子:「你有沒有讀過那種 if 包 if 包 if、核心邏輯縮在第七層縮排裡的函式?讀到一半已經忘了前面的條件是什麼。今天教你怎麼把它攤平。」
🎙️ 自問自答:「是不是看到 switch 就該用多型把它幹掉?」——不是。如果這組分流反覆出現、而且常常要新增型別,多型很划算;但如果只出現一兩次、邏輯又簡單,硬拆成一堆子類別反而讓你得開七個檔案才看懂一件事。用判斷力,別當教條。
🎙️ 帶走的一題:「找一個巢狀的 if,試著把『異常情況』用提早 return 在開頭打發掉。改完你會發現,函式主體變成一條清楚的主線——而且你少縮排了好幾層。」
更大範圍關聯
- OO vs FP 的條件邏輯之爭:Replace Conditional with Polymorphism 是這場長年辯論的縮影。OO 用多型分散、FP 用模式比對(pattern matching)集中——兩種都在解「按型別分流」,取捨不同。這是這集最好的延伸討論。
- 與《Clean Code》的呼應與爭議:Uncle Bob 同樣主張「多型優於 switch」,也同樣在這點上被批評(效能、可讀性分散)。兩本經典在此立場一致、也一起承受質疑——適合並排討論(見 [軟體工程領域切分藍圖])。
- 與型別系統的演進:Introduce Special Case / Null Object 的精神,在現代語言裡升級成
Option/Result型別與 sum type;「用型別消滅特殊值檢查」是條從設計模式走向語言內建的清晰演化線。 - 開閉原則(OCP):用多型取代條件,正是為了「新增型別時對擴充開放、對既有 code 修改封閉」——SOLID 的 O 在這招上現形。
錄製建議
- 建議時長:約 20 分鐘。配比:Decompose / Consolidate Conditional 約 4 分、Guard Clauses 約 5 分、Replace Conditional with Polymorphism(含爭議)約 7 分、Introduce Special Case / Assertion 約 4 分。
- 討論策略:這集很「解氣」,每招都有清楚的「亂 → 清」對照,講起來爽快,多用前後對比讓聽眾有畫面。Guard Clause 是最該大力推廣的一招(最好落地)。Polymorphism 那段務必把「這是爭議點、Fowler 自己也說要用判斷力」攤開講,這是整集的智識亮點,也呼應整季「帶腦讀經典」的立場。Null Object 接到 Option/Result 顯縱深。
- 把握「條件邏輯=替讀者管理注意力」這個觀點,讓這集不只是招式集,而是一堂關於「怎麼讓判斷邏輯好讀」的設計課。