Section 1:為什麼要學設計模式
設計模式(Design Patterns)是「前人總結的最佳實踐」。它們不是為了用而用, 而是針對特定問題的成熟解法。重要的是,你不需要背起來每一個模式——你需要的是當問題出現時, 知道有這類解法的存在。
「設計模式就像烹飪食譜。你不需要背起來,但當你遇到相同問題時,知道有這個食譜存在就夠了。」
最常見的學習誤區
❌ 錯誤思維
「這段程式碼要套哪個 Pattern?」
從 Pattern 出發,強行套用,反而讓程式碼變複雜
✅ 正確思維
「我遇到了什麼問題?有沒有成熟的解法?」
從問題出發,Pattern 只是工具,不是目的
這篇文章選了面試最高頻的四個模式:Observer、Strategy、Factory、Singleton。 每個都會先說「問題是什麼」,再帶出「Pattern 怎麼解決它」,最後給出 React 中的實際應用。
Section 2:Observer Pattern(觀察者模式)
核心問題:如何讓多個「訂閱者」在某個事件發生時自動收到通知,而不需要讓發布者知道有哪些訂閱者?
你在 React 中已經在用 Observer 了
useEffect的 deps array — 觀察資料變化,當依賴改變時觸發Redux store + useSelector— store 是 Subject,每個 selector 是 ObserverEventEmitter(Node.js)— 最經典的 Observer 實作
從零實作 TypeScript 型別安全的 EventEmitter
下面這個實作有幾個設計亮點:泛型讓事件名稱和 payload 都有型別推斷、on() 回傳取消訂閱函式避免 memory leak、 完整的 TypeScript 泛型約束確保你不會傳錯 payload。
React Custom Hook 版本
在 React 中,通常會把 EventEmitter 放進 Ref 裡,確保同一個元件生命週期內是同一個實例。
Observer 的核心價值:發布者(Publisher)和訂閱者(Subscriber)完全解耦。 發布者不需要知道有誰在訂閱,新增訂閱者完全不影響發布者的程式碼。 這就是「Open for extension, Closed for modification」(開放封閉原則)的體現。
Section 3:Strategy Pattern(策略模式)
核心問題:同一個操作有多種實作方式,如何在執行時動態切換,而不需要修改呼叫端的程式碼?
常見場景
❌ 沒有 Strategy Pattern(if-else 地獄)
每加一種支付方式,這個函式就越來越複雜、越來越難測試,最終變成一個誰都不敢改的巨大函式。
✅ Strategy Pattern
React 中的 Strategy Pattern
在函式型程式設計(Functional Programming)中,Strategy 就是把「函式當作參數傳遞」。 React 的 Hook 很適合這個模式。
Section 4:Factory Pattern(工廠模式)
核心問題:建立物件的邏輯複雜,或需要根據條件建立不同類型的物件時, 如何讓呼叫端不需要知道建立細節?
常見場景
- 根據環境建立不同的 Logger(dev console vs prod Datadog)
- 根據角色建立不同的 User 物件(Admin vs Guest vs Member)
- 根據設定建立不同的資料庫連線(PostgreSQL vs SQLite)
Simple Factory — Logger 範例
React 中的 Factory Pattern
動態表單是 Factory Pattern 在 React 中最典型的應用場景。 後端傳來欄位設定,前端透過 Factory 元件決定渲染哪種輸入元件。
Section 5:Singleton Pattern(單例模式)
核心問題:整個應用只需要一個實例(例如資料庫連線池、設定管理、全域狀態), 如何確保不會意外建立多個?
❌ 沒有 Singleton 的問題
✅ Singleton Pattern
注意:Singleton 在多執行緒環境需要額外的「雙重檢查鎖定」處理, 但在 Node.js 單執行緒環境中不需要擔心這個問題。 在 React 前端,全域狀態管理(Redux store、Zustand store)本質上也是 Singleton。
Section 6:實際面試情境
面試中被問到「請解釋一個你用過的設計模式」是高頻題,關鍵是要用「STAR 法則」回答: Situation(情境)、Task(任務)、Action(你的解法)、Result(結果)。
問:「請解釋一個你用過的設計模式」
「在我的電商專案中,我使用了 Strategy Pattern 來處理不同的支付方式。
問題是最初用 if-else 寫,每次要加新的支付方式就要修改核心函式, 每次改動都有可能影響其他支付方式的邏輯,也很難寫單元測試。
我改成 Strategy Pattern 後,每種支付方式是獨立的 class,都實作同一個PaymentStrategy 介面。 加新的支付方式只需要新增一個 class,核心的 PaymentContext 完全不用動—— 符合開放封閉原則。
測試也更容易——每個策略可以獨立做單元測試,測試信用卡策略時不需要顧慮 LINE Pay 的邏輯。」
四個模式的選擇指引
Observer
需要「一對多通知」,發布者不需要知道訂閱者
例:EventBus、DOM addEventListener、Redux
Strategy
同一個操作有多種算法/實作,需要執行時切換
例:支付方式、排序算法、認證策略
Factory
建立邏輯複雜,或需要根據條件建立不同型別
例:Logger、動態表單、資料庫連線
Singleton
整個應用只需要一個實例,避免重複建立
例:資料庫連線池、設定管理、全域 store