前提:GoF 的時代背景
GoF 四人幫(Gamma, Helm, Johnson, Vlissides)在 1994 年出版 Design Patterns: Elements of Reusable Object-Oriented Software 時,主流語境是 C++ 和 Smalltalk。那個時代的語言沒有一等函數(first-class functions)、沒有泛型(generics)、沒有自動記憶體管理、沒有模組系統。許多 GoF pattern 的存在,本質上是在補語言層級的缺失——用 class 的抽象間接層,換取那個時代語言本身無法提供的彈性。
Peter Norvig 在 1996 年的論文 “Design Patterns in Dynamic Languages”(http://norvig.com/design-patterns/)做了一個清晰的驗證:他檢視 Lisp 和 Dylan 等動態語言,發現 GoF 23 個 pattern 中有 16 個「不可見或更簡單」——不是因為那些問題不存在,而是因為語言本身已經把解法內建了。這個觀察在 2026 年的 TypeScript、Rust、Python、Kotlin 時代更加明顯。
問題框架不應該是「GoF 過時了嗎?」,而是兩個更精準的問題:第一,哪些 pattern 已經被現代語言特性吸收,直接用語言就能表達?第二,哪些 pattern 的意圖(intent)和解決的張力(forces)至今仍然真實,只是實作形式隨語言演進而改變?搞清楚這兩類,才能在現代專案中對 GoF 做出有根據的取捨。
第一類:已被語言特性吸收的 Pattern(直接使用語言即可)
Iterator
現代狀態:被語言內建迭代協議吸收。JavaScript 的 for-of + Symbol.iterator、Python 的 __iter__ / __next__、Java 的 Stream API、Rust 的 Iterator trait——Iterator pattern 已成為語言一等公民。Generator 函數進一步讓惰性迭代(lazy iteration)變成語言基本功能,不需要手動維護迭代狀態。
在 GoF 的時代,需要自行設計 Iterator interface 和 ConcreteIterator class,才能讓集合的遍歷與其內部表示解耦。現代語言已把這個解耦內建在語言規範裡。在現代 codebase 裡看到手工實作 GoF Iterator class 的程式碼,幾乎可以確定是過度工程。
Singleton
現代狀態:在模組系統下,模組本身就是 singleton。ES module 的頂層 export、Python 的 module 匯入(同一 module 只執行一次)、Node.js 的 require cache——「全局唯一實例」這個需求,現代模組系統已自然滿足。
需要更複雜生命週期管理的「唯一實例」需求,現在由 DI container 管理(Spring、Angular 的 @Injectable({ providedIn: 'root' }))。GoF 的 getInstance() 靜態方法實作方式在現代已被視為反模式:它引入全局可變狀態,讓單元測試無法注入替換實例。當你真的需要 singleton 語義,模組層級的 export 就夠了,不需要 getInstance()。
Abstract Factory
現代狀態:泛型(Generics)+ 依賴注入讓「建立一族相關物件」不再需要複雜的 abstract factory class 層次。GoF 的 Abstract Factory 是在沒有泛型的語言裡用 abstract class 提供型別安全的物件建立協議。
TypeScript 的 generic function + conditional types、Rust 的 trait bounds + associated types、Java 的 Generics + Spring DI,讓工廠邏輯用更簡潔的形式表達。Factory Method 也類似——高階函數直接取代「定義一個建立物件的 virtual method 讓子類覆寫」的需求。當你在 TypeScript codebase 看到 AbstractWidgetFactory + ConcreteWindowsFactory + ConcreteMacFactory 的三層結構,先問是否 generic function 配合型別參數就能解決。
Template Method
現代狀態:高階函數(Higher-Order Functions)取代。Template Method 用 abstract class 讓子類填入「空白步驟」——固定骨架,可變步驟由繼承覆寫。這個意圖完全可以用「傳入函數作為參數」表達,且更靈活。
JavaScript 的 callback / closure、Rust 的 closure + trait object、Python 的 callable 參數——讓 template method 的意圖(固定演算法骨架 + 可變步驟)用函數組合自然表達,不需要建立 class 層次。繼承在這裡帶來的問題(脆弱基底類別、難以組合多個「步驟變體」)被函數組合完全規避。
第二類:換形式存活的 Pattern(意圖不變,實作面貌改變)
Observer → 響應式流 / Signals
GoF Observer:Subject 維護 observer list,狀態改變時手動通知每個 observer。
現代形式走三條路:
第一條是 RxJS(https://github.com/ReactiveX/rxjs)把 observer 推廣成 Observable stream,支援 compose、backpressure、error handling,把原本手動管理的 observer list 變成可組合的資料流管線。
第二條是 Solid.js / Preact Signals 把 signal 的自動依賴追蹤推到 UI 框架核心——不需要手動 subscribe/unsubscribe,runtime 自動追蹤哪個計算依賴哪個 signal;Vue 3 的 reactivity system 走相同方向。
第三條是 Node.js EventEmitter,保留了最接近 GoF 原始形式的 observer 實作,用在事件驅動的 I/O 場景。
Observer 的意圖(一對多通知,解耦 subject 和 observer)完整存活,但實作從「手動維護 observer list + 手動 notify」變成 reactive runtime 自動管理依賴圖。
Strategy → 一等函數
GoF Strategy:定義一族演算法,封裝成 class,可互換。意圖是讓演算法獨立於使用它的客戶端變化。
在有一等函數的語言裡,Strategy 就是傳入一個函數。TypeScript 的例子直接:
const sort = <T>(data: T[], compareFn: (a: T, b: T) => number): T[] =>
[...data].sort(compareFn);compareFn 就是 Strategy,不需要 Comparator interface + AscendingComparator class + DescendingComparator class 三個檔案。
保留 Strategy 作為 class/interface 的時機:strategy 需要持有狀態(不只是一個函數)、需要透過 DI container 注入(Spring 的 @Service)、需要在多個 method 間共享 context。在這些情況下,Strategy interface 提供的明確契約仍然有工程價值。
Command → Promise / Task Queue / CQRS Command
GoF Command:把操作封裝成物件,支援 undo/redo/queue/log。
現代形式走三條路,視使用場景而分叉:
第一條是 Promise / async-await:「延遲執行」和「結果等待」的語義被語言層級的非同步原語直接表達,不需要 Command 物件來包裝。
第二條是訊息佇列中的 message 就是 Command——CQRS(Command Query Responsibility Segregation)的 Command side,把 write intent 序列化成訊息發往 command bus,是 GoF Command 在分散式系統層面的直接延伸,見 Event Sourcing + CQRS 後端落地。
第三條是 Undo/Redo 場景:Command pattern 仍然是最佳解法,且基本照搬 GoF 原形。CodeMirror(https://github.com/codemirror/dev)的 transaction 系統、ProseMirror 的 step 系統——編輯器把每個編輯操作封裝成可逆的 Command 物件,這是 GoF Command 原始意圖的直接應用。
Decorator → Middleware / HOC / 語言 Decorator 語法
GoF Decorator:動態為物件增加職責,包裝層不改變被包裝者的介面。
現代形式多樣化,但意圖統一:
Express.js / Koa 的 middleware chain 是函數式的 Decorator——每個 middleware 包裝 next,可以在前後插入行為,且不修改 route handler 本身。
React 的 Higher-Order Component(HOC)是元件級 Decorator——withAuth(Component) 回傳一個帶 auth 邏輯的新元件,原始元件不知道自己被包裝。
Python 的 @decorator 語法和 TypeScript 的 @Injectable()、@Controller() 把 Decorator 提升為語言語法,讓橫切關注點(cross-cutting concerns)的注入在語法層面就清晰可見。
Spring AOP 的 @Aspect 是更激進的形式——在編譯或運行期把 decorator 行為織入(weave)目標,被裝飾的物件甚至不知道自己被裝飾。
State → Reducer / State Machine
GoF State:物件的行為隨內部狀態變化而改變,把每個狀態封裝成 class,把狀態轉換邏輯分散在各 State class 裡。
現代形式走兩條路:
Redux 的 reducer (state, action) => newState 是 State pattern 的函數式版本——狀態集中管理,每個 action type 對應一個狀態轉換分支。比 GoF State 更容易測試(純函數),但沒有 GoF State 的「合法轉換」約束。
XState(https://stately.ai/docs/xstate)把 State pattern 形式化為 statechart(Harel statechart),比 GoF State 嚴謹得多:明確宣告所有合法狀態、所有合法事件、每個狀態下每個事件的合法轉換——不在宣告範圍內的轉換不會發生,徹底防止 impossible states。GoF State 在前端幾乎被 reducer / state machine 取代;在後端,CQRS aggregate 的狀態轉換是 State pattern 的直接延伸。
Chain of Responsibility → Middleware Pipeline / Interceptors
GoF Chain of Responsibility:請求沿著處理者鏈傳遞,直到有人處理。意圖是解耦請求的發送者和接收者,讓多個物件都有機會處理請求。
現代形式:Express / Koa 的 middleware、gRPC interceptors、HTTP client interceptors(Axios interceptors)、Spring 的 HandlerInterceptor——所有這些都是 Chain of Responsibility 在不同協議層的具體化,且比 GoF 的 class-based chain 更容易組合和動態配置。函數式組合(compose / pipe)讓 chain 的建構變成聲明式:pipe(auth, rateLimit, validate, handler)。
第三類:基本保持原形存活的 Pattern
Facade
持續有效,形式幾乎不變。複雜子系統前的簡化介面永遠需要——SDK wrapper、API client、service layer——都是 Facade 的直接應用。Stripe SDK 就是對複雜 Stripe REST API 的 Facade;AWS SDK 就是對 AWS API 的 Facade。
唯一的演進是 Facade 現在幾乎都是 async 的(因為底層操作大多是 I/O),以及 TypeScript interface 讓 Facade 的公開 API 契約更嚴格。
Adapter
持續有效,形式幾乎不變。第三方 API 整合、legacy system 橋接、不兼容介面之間的橋樑——這個需求永遠存在。TypeScript interface 讓 adapter 的型別檢查更嚴格,減少執行期的不匹配錯誤。
Composite
持續有效,且在 UI 框架時代更加普遍。React / Vue / Angular 的 component tree 就是 Composite pattern 的直接應用——葉節點(leaf component)和容器節點(container component)對外呈現相同的 component 介面。AST(抽象語法樹)是另一個典型的 Composite 應用,每個 AST 節點可以是葉節點(literal)或容器節點(expression / statement)。
Proxy
持續有效,且形式在語言和架構兩個層面都有豐富展開。
語言層:JavaScript ES6 的 Proxy 物件是語言級的 Proxy pattern,讓你用統一介面攔截物件的任意操作(get、set、has、apply)。
架構層:API gateway 是服務間通信的 Proxy;service mesh 的 sidecar proxy(Envoy)是網路層的 Proxy;CDN edge 是 HTTP 請求的 Proxy。
Proxy 的意圖(用相同介面控制對另一物件的存取,可在存取前後插入邏輯)在從語言到基礎設施的每一層都有對應的實體。
第四類:在 AI coding 時代的新意涵
AI 對「顯性結構」更友善。 Strategy、Command、State Machine 等有明確 interface 和邊界的 pattern,AI 生成正確程式碼的機率更高——因為 AI 能從介面宣告推斷出實作的形狀和約束。相反地,Singleton(全局可變狀態)、過度嵌套的 Decorator chain、濫用 Template Method 的繼承層次,讓 AI 生成的程式碼難以預測,也難以讓 AI 正確推理副作用邊界。選擇有顯性結構的 pattern,不只是對人類工程師友善,也是對 AI 協作友善的設計。
過度 pattern 化是 AI 的特定風險。 AI coding 工具傾向生成「看起來像教科書範例」的 pattern 實作——在只需要一個高階函數的地方引入 Strategy interface + 兩個 ConcreteStrategy class;在只需要 module export 的地方生成 getInstance() 的 Singleton;在只需要直接繼承的地方生成 Abstract Factory 層次。這種過度設計在 AI 生成的程式碼裡出現頻率明顯高於人工撰寫。工程師的職責之一是識別並拒絕這類過度設計,用 code review 的眼光問:「這個 pattern 解決了什麼 force?去掉它會有什麼問題?」
新興的 AI-native pattern。 Prompt Template Pattern(參數化 prompt,把可變部分與固定結構分離)、RAG Pattern(Retrieval Augmented Generation,把知識庫檢索結果注入 context)、Judge/Evaluator Pattern(用 LLM 評估另一個 LLM 的輸出品質)——這些是程式設計 pattern 的新領域,解決的問題在 GoF 時代根本不存在。目前尚無 GoF 層次的系統化整理和語言獨立的形式定義,見 AI Agentic SaaS 工程實踐。
實踐總結:如何在現代專案中對待 GoF
不要為了用 pattern 而用 pattern。 先識別問題和 forces(flexibility vs simplicity、reuse vs coupling),再找解法——解法可能是 pattern,可能是語言特性,可能是兩者的組合。
先問語言能解嗎。 「這個需求用一等函數能解嗎?」比「這個需求該用 Strategy pattern 嗎?」更好的第一問題。如果語言特性足夠,引入 pattern 只是增加間接層和認知負擔。
術語有溝通價值,但術語不等於必須照搬實作。 說「這裡用 observer 模式」比「這裡用回調通知」更精確,溝通效率更高。GoF 的術語作為工程師之間的共同語言,在 2026 年仍然有效。但術語的有效性不等於必須照搬 GoF 的 class 層次實作——RxJS Observable 是 Observer,即使它的實作與 GoF 的 Subject/Observer class 完全不同。
理解 forces 才是核心。 每個 GoF pattern 都是在解決特定 forces 之間的張力。先理解 forces(什麼是這個設計脈絡下真正的張力),再選 pattern,才不會用錯工具。Singleton 解決的 force 是「確保唯一實例 + 全局存取點」,但如果你的 force 只是「避免重複初始化」,module-level export 更簡單。
延伸閱讀
- GoF 原書:Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides (1994)
- Peter Norvig, “Design Patterns in Dynamic Languages” (1996) — http://norvig.com/design-patterns/
- Refactoring.Guru 的 Pattern 圖解(非 GoF 原始資料,但可視化好):https://refactoring.guru/design-patterns
與既有專欄的關係
Command pattern 的 CQRS 延伸見 Event Sourcing + CQRS 後端落地;State pattern 的 State Machine 延伸見 前端 Pattern 高級詳解(State Machines 段落);AI coding 時代的方法論見 Engineering AI Coding Methodology。
更新紀錄
- 2026-07-03 — 建立。分析 GoF 23 pattern 中具代表性 pattern 的現代演進:已吸收進語言特性、換形式存活、基本保持原形,以及 AI coding 時代的新意涵。