第 9 / 10 部 待錄

設計模式消失還是換形

《Functional Design》Podcast 準備稿:設計模式消失還是換形

書名: Functional Design(Principles, Patterns, and Practices) 作者: Robert C. Martin(Uncle Bob) 系列: Reading Uncle Bob(恩普拉氏) 公開標題建議: Reading Uncle Bob|Functional Design.GOF 模式在 FP 是消失還是換形 (9/10) 涵蓋範圍: 第五部導言 + 第16章 設計模式回顧

背景速覽

這是全季爭議性最高、也最過癮的一集。函式式社群有句流傳很廣的話:「設計模式是 OO 語言缺陷的繞路,函式式語言根本不需要它們。」Uncle Bob 在這集要正面迎戰這個說法。他逐一檢視 GOF(四人幫,1994 年《設計模式》)的經典模式——Abstract Server、Adapter、Command、Composite、Decorator、Visitor、Abstract Factory——在 Clojure 裡到底是「消失了」還是「換了個形狀」。對 Reading Uncle Bob 系列的聽眾,這集是思想的收束:他用整本書積累的工具(多型、不可變、依賴反轉、架構邊界),重新詮釋了軟體業最深刻的觀念之一。

一句話重點

「FP 不需要設計模式」這個說法部分對、整體錯——某些 GOF 模式確實含有「繞開 OO 語言限制」的腳手架(比如 Visitor 的雙重分派),那部分在 FP 會消失;但模式背後的通用解法幾乎全都還在,只是換用函式、map、multi-method 來表達。模式不是消失,是換形。

值得討論的重點

1. 模式是「有名字的解法」——名字本身就是價值

先把設計模式講對:它是「在特定脈絡下,對某個常見問題的、有名字的解法」。Uncle Bob 強調,模式的價值在於名字和形式都是約定俗成的——熟悉同一套術語的人,只要說「我們用 Abstract Server 吧」,對方立刻聽懂「在 client 和 server 之間插一個介面」。這是模式最被低估的好處:它是團隊的共同語言。他用 Switch/Light 的老故事帶出 Abstract Server(同時是 DIP、OCP、LSP 的最簡實現),再帶出當 Light 不能改時要用的 Adapter。

2. 函式即物件:Command 模式幾乎「蒸發」了

GOF 裡 Uncle Bob 最愛的 Command 模式——一個只有 execute() 方法的介面——在 FP 裡幾乎不見了,因為函式式語言的函式本身就是物件。要傳一個 Command?直接傳函式。要帶參數的 Command?用 partial 把函式和參數綁一起。這完美示範了「模式換形」:簡單的 Command 直接被「函式」這個語言特性吸收掉。但 Uncle Bob 很誠實地補刀——一旦需求變複雜(要多型、要 undo),你又會自然回到 GOF 的標準形式(multi-method + 回傳記錄結果的新物件)。模式沒死,只是在簡單情況下隱身了。

3. Visitor 的「90 度旋轉」消失了——揭穿 GOF 的過度設計

這集最有洞見的橋段。Visitor 模式在 Java 裡極其複雜:要 accept 方法、要雙重分派(double-dispatch)、要把「子型別」轉成「方法名」(Uncle Bob 叫這個「90 度旋轉」)。為什麼這麼繞?答案一針見血:因為 closed classes——Java/C++ 不能從外部給類別新增方法,只能打開原始碼改。而 Clojure 用 defmulti to-json 一行就把新功能加進型別模型,完全不必碰原本的 shape/circle/square。GOF 的 accept + 雙重分派全部蒸發——「它們本來就只是繞開 closed classes 的腳手架」。Uncle Bob 由此下了個大膽判斷:GOF 把 Visitor 描述得有點過度了,雙重分派只是 closed classes 語言的副產品

注意事項

⚠️ 這集的「FP 不需要設計模式」是個假命題,要拆成兩半精準回答:(1)對的部分——某些模式含「繞語言限制」的成分(Visitor 雙重分派、部分 Adapter),FP 裡會消失;(2)錯的部分——模式背後的「問題 + 通用解法」幾乎全在。別讓聽眾走出去只記得「FP 不用模式」或「FP 還是要用模式」任一極端,真相是「換形」。

⚠️ 模式太多(七八個),絕不要每個都平均講。挑三個有故事性的講透:Command(函式即物件,最能體現「換形」)、Visitor(90 度旋轉消失,最有洞見)、Abstract Factory(不透明 token、型別安全取捨,最有深度)。其餘(Composite、Decorator、Adapter)一句話帶過共性即可。

⚠️ Abstract Factory 那段藏著一條「軟體物理定律」級的洞見,別漏:任何語言要在架構邊界上維持 OCP,都得在邊界處放棄部分型別安全、依靠 runtime 例外。用「不透明 token」(:square/:circle,邊界上方完全不認識它們)消除 90 度旋轉,代價就是型別系統管不到它、連 spec 也救不了。這是設計取捨的本質,很值得一講。

⚠️ 這集尾巴有個「後記:OO 是毒藥嗎」,是整本書「FP 與 OO 互補」論點的總結陳詞,務必講到——它是這集、甚至整季的思想高潮。

專家補充

💡 「設計模式是語言缺陷的補丁」這個說法,最早是 Paul Graham 在《On Lisp》和相關文章裡的論調——他主張 Lisp 的巨集和高階函式讓很多模式「不必要」。Peter Norvig 也有著名演講說 GOF 23 個模式有 16 個在動態語言裡「不可見或更簡單」。Uncle Bob 這章其實是在和這整個 Lisp 傳統對話——他部分同意(模式會換形),但反對「所以模式沒用」的結論。把這個學術脈絡點出來,這集會很有層次。

💡 Visitor 揭穿「90 度旋轉只是 closed classes 的副產品」,其實觸及一個著名的學術問題——Expression Problem(表達問題),由 Philip Wadler 命名。它問的是:能不能在「不修改既有程式碼」的前提下,既加新型別、又加新操作?OO 容易加型別、難加操作(要改每個 visitor);FP/函式容易加操作、難加型別。Clojure 的 multi-method 用「open class + 多重分派」在這個問題上取得了罕見的雙贏。Uncle Bob 沒點名 Expression Problem,但他講的正是它——懂的聽眾會會心一笑。

💡 結尾「OO 是毒藥嗎」的回答是整本書的論點凝結:Clojure 其實是個「OO/函式式語言」——它有多型(至少三種機制)、有封裝(map 即物件)、有構造函式、有和 Java 高度相似的 namespace 架構切分。FP 帶來的「額外限制」只是消除或嚴格隔離副作用、強烈偏好不可變物件——但它們仍然是物件。所以 OO 那些有用的設計原則與模式,在 FP 裡多半依然適用。這徹底拆掉了「FP vs OO」的對立框架,是 Uncle Bob 晚年最重要的思想姿態。

討論問題

🎙️ 開場鉤子:「函式式圈子有句名言:『設計模式是物件導向語言缺陷的繞路,我們函式式不需要那套。』這話對嗎?今天 Uncle Bob 要逐一檢驗 GOF 那些經典模式,看它們在函式式世界到底是真的消失了,還是只是換了張臉。」

🎙️ 自問自答:「為什麼 Visitor 模式在 Java 裡要搞得那麼複雜,又是 accept 又是雙重分派?」——因為 Java 的類別是『關閉』的,你不能從外面給它加方法,只能繞。Clojure 的類別是『開放』的,一行就把新操作加進去——那些複雜的腳手架,本來就只是為了繞開語言限制而存在的。

🎙️ 帶走的一題:「下次你在 FP 語言裡覺得『這個模式好像用不上』——先別急著說模式沒用。問自己:是這個模式背後的問題消失了,還是只是它的形狀換了(被函式、被 map、被 multi-method 吸收了)?多半是後者。」

更大範圍關聯

  • 本書設計模式部:這集是第五部的核心,把整本書的論點(FP 與 OO 互補)推向高潮——連 GOF 模式都能在 FP 重現。
  • GOF 原典:《Design Patterns: Elements of Reusable Object-Oriented Software》(1994,四人幫)是這集的對話對象,與《Clean Code》《重構》同列工程必讀。
  • Lisp 傳統的對話:Paul Graham《On Lisp》、Peter Norvig「Design Patterns in Dynamic Languages」演講,是「模式是語言缺陷」說法的源頭——Uncle Bob 在和這整個傳統辯論。
  • Expression Problem:Philip Wadler 命名的學術經典問題,Visitor「90 度旋轉」那段正是它的具體化;Clojure multi-method 是著名解法之一。用 [軟體工程領域切分藍圖] 看,這集橫跨「設計模式」與「語言設計」兩支。

錄製建議

  • 建議時長:約 25 分鐘(單章但份量最大、爭議性最高,值得多給時間)。配比:什麼是模式 + 為何有「模式無用論」約 5 分、Command(函式即物件)約 6 分、Visitor(90 度旋轉消失)約 7 分、Abstract Factory(不透明 token + 型別安全取捨)約 4 分、「OO 是毒藥嗎」總結約 3 分。
  • 討論策略:開場用「FP 不需要模式」這個業界爭議當鉤子,立刻有立場、有張力。Command 講「函式即物件」的優雅、再補 Uncle Bob「複雜了就回到標準形式」的誠實。Visitor 是全集智慧結晶,把「90 度旋轉只是 closed classes 的腳手架」講透。結尾一定要用「OO 是毒藥嗎」收束,把「FP 與 OO 互補」這條貫穿全季的主線推到最高點。
  • 這是收官前的思想高潮集,講完讓聽眾帶走一句總結:「模式不會消失,它只是換形——而能看出『同一個問題在不同典範下的不同臉孔』,正是資深工程師的功力。」