《Functional Design》Podcast 準備稿:SOLID 在函式式世界
書名: Functional Design(Principles, Patterns, and Practices) 作者: Robert C. Martin(Uncle Bob) 系列: Reading Uncle Bob(恩普拉氏) 公開標題建議: Reading Uncle Bob|Functional Design.SOLID 不是 OO 的專利 (6/10) 涵蓋範圍: 第12章 SOLID 原則
背景速覽
這是整本書的旗艦集,也是 Uncle Bob 寫這本書最想證明的一件事。SOLID 五大原則是他二十多年前在 OO 脈絡下提出的——以至於很多人把 SOLID 跟 OO 綁死,甚至認為它和函式式編程相剋。這集他要逐條拆解這個誤解,論證:SOLID 是通用的軟體設計原則,與特定典範無關。對 Reading Uncle Bob 系列的聽眾來說,這集格外有意義——SOLID 是 Uncle Bob 一生的招牌,看他親手把它搬進 FP 世界,等於看宗師驗證自己理論的普適性。
一句話重點
SOLID 講的從來不是「類別」這個語言特性,而是模組與依賴——所以它在沒有類別、沒有繼承的函式式世界裡依然完整適用,只是把「多型介面」換成高階函式、vtable、multi-method 或 protocol 來表達;最終你會切出一個「看起來很像 OO、卻完全是函式式」的乾淨架構。
值得討論的重點
1. SRP 與 OCP:人,與「對擴展開放」
SRP(單一職責)的核心被 Uncle Bob 講得很精準:模組應該對少數的「變更來源」負責,而那些來源最終都是人(角色/actor)。一個模組若對多個 actor 負責,他們的需求變動會在你的程式裡互相打架,造成脆弱性——「修一個小功能就讓系統別處掛掉」是主管最怕的事。他用「把信用額度上限規則塞進解析函式」當反例:解析(語法)和商業規則(語意)是兩個 actor,揉在一起就會互相干擾。OCP(開放封閉)則要「對擴展開放、對修改封閉」——關鍵是高階政策透過抽象層存取低階細節。FP 至少有三種方式做這個抽象層:高階函式(把函式當參數傳)、vtable(把函式打包成 map)、multi-method。
2. LSP 的陷阱:正方形不是長方形(在程式裡)
這集最精彩的橋段。LSP(Liskov 替換)說「子型別必須能替換基底型別」。Uncle Bob 用經典的正方形/長方形打臉「ISA 直覺」:幾何上「正方形是長方形」沒錯,但程式裡的 square 物件和 rectangle 物件行為不同(長方形的高寬可獨立改,正方形不行),所以不該建立子型別關係。他提出 Representative Rule(代表規則):「物件代表的事物之間的關係,不一定是物件之間的關係。」這是個深刻的設計洞見——看到現實世界的 ISA 關係時,先別急著建子型別,先問:行為一致嗎? 還有一個連鎖警告:每一個 LSP 違反都是潛在的 OCP 違反——你會被迫去改高階政策來「補丁」低階的錯誤行為。
3. DIP 是這一切的底層機制:把依賴方向反轉
Uncle Bob 給 SOLID 五原則排了座次:OCP 是道德核心、SRP 是組織原則、LSP 和 ISP 是避坑警告、而 DIP(依賴反轉)是讓這一切成為可能的底層機制——幾乎每個原則違反,都能透過反轉某個關鍵依賴來修復。做法是在高階與低階之間放一個抽象介面,兩者都依賴它,但讓原始碼依賴方向指向抽象。這就能做出「外掛式架構」:商業規則在中央,UI 和資料庫在外層當插件。他用 Martin Fowler《重構》的影片出租店範例,一步步從「全擠一起」拆到「11 個模組的乾淨架構」,最後用 derive 表達 ISA、只覆寫一個函式就加上「買二送一」新政策。整個結構「看起來很像 OO,但完全是函式式」。
注意事項
⚠️ 這集資訊量是全季最大的(單章一萬三千多字),務必取重點、不貪全。五個原則不必平均用力——SRP/OCP 講通、LSP 用正方形例子講透、DIP 講「反轉依賴 + 外掛架構」這個大圖即可。ISP 一句話帶過(「不要依賴你不需要的東西」,動態語言違反成本較低)。
⚠️ FP 做 OCP 的「三種方式 + protocol」容易講成技術流水帳。聽眾不需要記住高階函式/vtable/multi-method 的語法差異,只要懂一個結論:OO 用『多型介面』做抽象層,FP 有好幾種等價手段做同一件事。抽象層的「概念」比「實作手段」重要。
⚠️ Uncle Bob 有句話要原汁原味轉述:談到 protocol+record 很像 OO 時,他說「那是因為它就是 OO——JVM 是 OO 基礎,Clojure 在它上面合作良好」。這句話是「FP 與 OO 互補」最直白的證據,別漏掉。
專家補充
💡 「Representative Rule」是這本書送給聽眾最值錢的一條原則,而且超越 FP/OO 之爭。它戳破了物件導向教學裡一個流毒——「用 ISA 找繼承關係」。現實世界的分類學(企鵝是鳥、正方形是長方形)和程式裡的子型別,遵守的是不同規則:前者看「本質」,後者看「行為可替換性」。很多繼承地獄都源自把這兩者搞混。這條洞見比任何 SOLID 字母都實用。
💡 把 SOLID 從 OO 「解綁」這件事,其實是 Uncle Bob 對自己歷史定位的一次澄清。他二十多年前提 SOLID 時用 OO 舉例,結果整個業界把 SOLID=OO 設計畫上等號。這本書等於宗師回頭說:「你們誤會了,我講的是依賴與模組,不是類別。」這對任何學過 SOLID 卻覺得「FP 沒有類別所以用不上」的工程師,是一次重要的解放。
💡 DIP 與「外掛式架構」直接就是《Clean Architecture》(Reading Uncle Bob 第二本)的核心——商業規則在中心、框架/資料庫/UI 在外圈當可替換插件,依賴方向一律指向內。這集等於用 FP 重講了一遍 Clean Architecture,證明那套架構思想不依賴 OO。聽過第二本的聽眾會有「原來殊途同歸」的爽感。
💡 「每個 LSP 違反都是潛在的 OCP 違反」這個連鎖關係,揭示 SOLID 不是五條孤立規則,而是一張互相牽連的網。違反 LSP → 被迫加 if 補丁 → 那個 if 違反 OCP → 想刪掉 if → bug 復活。這種「設計原則彼此咬合」的視角,是資深工程師和初學者的分水嶺。
討論問題
🎙️ 開場鉤子:「SOLID,五個字母,大概是軟體設計界最有名的縮寫。很多人以為它是物件導向專屬的——今天提出 SOLID 的本人要親自告訴你:你們都搞錯了,它根本不是 OO 的專利。」
🎙️ 自問自答:「正方形不是長方形嗎?國小數學不是這樣教的?」——數學上是,但程式裡不是。因為程式裡的物件比的是『行為能不能替換』,不是『概念上像不像』。長方形能獨立改高寬,正方形不能——所以在程式裡,正方形不該是長方形的子型別。
🎙️ 帶走的一題:「下次你準備寫 class B extends A,因為『B 是一種 A』——先停一下,套用 Representative Rule 問自己:B 物件的行為,真的能在所有用到 A 的地方無痛替換嗎?如果不行,分開放,別硬繼承。」
更大範圍關聯
- 本書設計核心:這集是第三部「函式式設計」的重頭,把前五集累積的觀察(不可變、多型、資料流)收斂成「FP 也能做嚴謹架構」的論證。
- 與《Clean Architecture》互文:DIP + 外掛架構就是 Reading Uncle Bob 第二本的靈魂;這集等於用 FP 重新驗證了那套思想,是系列內部最強的呼應之一。
- SOLID 的歷史:OCP 源自 Bertrand Meyer(1988)、LSP 源自 Barbara Liskov(1988)、DIP 與「介面隔離」由 Uncle Bob 整合命名——這集等於一堂 SOLID 思想史。
- 領域座標:用 [軟體工程領域切分藍圖] 看,這集橫跨「設計原則」與「架構」兩大支,是把抽象設計原則落到 FP 實作的關鍵接點。SICP 講的「用抽象屏障管理複雜度」也是同一條脈絡。
錄製建議
- 建議時長:約 25 分鐘(單章但份量最大,這集可以稍長)。配比:為何 SOLID 通用約 3 分、SRP+OCP 約 7 分、LSP(正方形)約 7 分、DIP+影片出租店架構約 8 分。ISP 一兩句帶過。
- 討論策略:開場用「提 SOLID 的本人說你們誤會了」這個反差鉤住人。中段一定要把 LSP 的正方形/長方形例子講到位——這是全集最有記憶點、最能讓聽眾「啊原來如此」的橋段。DIP 別陷進 11 個模組的細節,講「反轉依賴 → 外掛架構 → 看起來像 OO 其實是 FP」這個大圖。
- 這集是旗艦,講完要明確收一句總綱:「SOLID 是軟體設計的原則,不是 OO 的專利——換了函式式的語言,它依然完整適用,只是換了表達方式。」這句話是整本書的論點核心。