在 JavaScript 開發中,行為型設計模式 (Behavioral Design Patterns) 是指專注於物件之間如何進行溝通、職責分配以及協作完成任務的一套設計方案。與創建型或結構型模式不同,行為型模式更關心的是「演算法」與「物件間的交互關係」。掌握這些模式能顯著提升程式碼的靈活性、重用性與可維護性,是進階開發者的必修課。
為什麼需要行為型設計模式?
當應用程式變得複雜時,物件之間的依賴關係往往會變得難以管理。行為型設計模式的主要優勢包括:
- 降低耦合度:讓物件不需要知道彼此的內部細節即可進行協作。
- 提升靈活性:可以在執行時動態改變物件的行為(如策略模式或狀態模式)。
- 結構清晰:將複雜的遍歷、通知或請求轉發邏輯從核心業務中分離出來。
本文將解析 5 種核心模式:Strategy、Iterator、State、Command 與 Observer,並結合 React 實戰範例。
- Strategy Pattern (策略模式):定義一系列演算法,將它們封裝起來,使它們可以互相替換,且不影響客戶端。
- Iterator Pattern (迭代器模式):提供一種方法順序訪問聚合物件中的各個元素,而又不暴露該物件的內部表示。
- State Pattern (狀態模式):允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類別。
- Command Pattern (命令模式):將一個請求包裝成一個物件,從而允許你使用不同的請求、佇列或日誌來參數化客戶端。
- Observer Pattern (觀察者模式):定義物件間一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都將得到通知並自動更新。
掌握這些行為型設計模式將幫助你更好地組織程式碼邏輯,處理複雜的互動行為,從而開發出更具彈性和可維護性的 JavaScript 應用。
Design Patterns 依照目的分成三群:
- Creational Patterns 創建型
- Structural Patterns 結構型,結構型設計模式根據實際情況會分成三種:
- Behavioural Patterns 行為型
1. Strategy Pattern (策略模式)
策略模式定義了一系列演算法,將它們封裝起來,使它們可以互相替換,而不影響客戶端。
Strategy Pattern (策略模式) 核心價值
透過將不同的行為封裝成獨立的策略物件,程式碼變得更加模組化。這在處理表單驗證、不同支付方式或用戶角色權限時非常實用。
React 實戰範例
在 React 中,我們可以根據 props 傳入的策略動態決定渲染邏輯:
1 | // 策略元件 |
Inversion of Control (控制反轉)
控制反轉是一種軟體設計原則,它提倡將控制權交給框架或容器,由它們來管理對象的建立和依賴關係。這種模式可以幫助實現程式碼的鬆耦合和可測試性。
原本的元件可以想像是一台車子,要控制車子就必須坐進去車子中,控制反轉是將車子設計成遙控車的概念,透過介面來進行控制。
底下舉兩個簡單的例子,透過把 normalize 的控制權放到 props 就能夠動態的去改動 input 文字大小寫。
1 | const Input = ({ normalize }) => { |
2. Iterator Pattern (迭代器模式)
迭代器模式提供一種方法來順序訪問聚合物件中的各個元素,而無需暴露該物件的內部表示。
Iterator Pattern (迭代器模式)核心價值
將遍歷邏輯與資料結構解耦。在現代 JavaScript 中,Symbol.iterator 讓任何物件都能成為可迭代對象,支援 for...of 語圈。
在 JavaScript 中,原生的 for...of 迴圈以及 Array.prototype.forEach、Map、Set 等都內建了迭代器概念。此模式的核心價值在於將遍歷邏輯與資料結構解耦,使得你可以對任何可迭代的資料結構使用統一的遍歷介面。
React 範例
React 的迭代器模式範例:
- ListComponent 接受 items 屬性後使用 map 函數來遍歷 items。
- IteratorComponent 透過
iterable[Symbol.iterator]()方法取到物件迭代器,將迭代器轉換為陣列並使用 map 函數遍歷每個元素。
1 | const IteratorComponent = ({ items }) => { |
3. State Pattern (狀態模式)
狀態模式允許一個物件在其內部狀態改變時改變它的行為。在 React 中,這與元件的生命週期與 useState 息息相關。
React 透過狀態和 setState 方法,提供便捷的方式來管理和更新狀態:
- Component State (元件狀態): 狀態是純 JavaScript 物件在元件內部進行初始化和更新,用於儲存元件所需數據
- setState (修改狀態的方法): setState 接受新的狀態物件或一個 callback function 來計算新狀態並更新元件,這個過程是非同步操作,會將更新一次性進行批次處理以提高性能
- Immutability (不可變性): 在更新狀態時,應該建立新的狀態物件,可以幫助 React 更好地處理狀態變化,進行有效的重新渲染,確保狀態的可預測性和性能優化
- State Lifting (狀態提升): 當多個元件需要共享狀態或進行狀態同步時,可以將狀態提升到共同父元件中,使狀態在元件樹中傳遞,從而實現元件間的數據的共用和溝通,使得元件間的狀態共享和溝通變得更加靈活和可控
State Pattern (狀態模式) 核心價值
避免大量的 if-else 或 switch。例如,一個訂單元件在「待處理」、「處理中」、「已完成」狀態下會有完全不同的按鈕與提示訊息。
狀態提升 (State Lifting) 範例
將狀態提升到父元件,讓多個子元件共用狀態:
1 | const App = () => { |
4. Command Pattern (命令模式)
命令模式將一個請求包裝成一個物件,從而允許你使用不同的請求、佇列或日誌來參數化客戶端。
Command Pattern (命令模式) 核心價值
最典型的例子是 Redux 的 action。我們發出一個命令(Action Object),由 Reducer(Receiver)來處理,這實現了「意圖」與「實作」的完全解耦。
1 | // Action Creator (建立命令) |
5. Observer Pattern (觀察者模式)
觀察者模式定義物件間一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都將得到通知並自動更新。
核心價值
它是響應式程式設計(Reactive Programming)的基石。在 React-Redux 中,useSelector 就是一種觀察者,當 Store 狀態改變時,它會通知元件重新渲染。
事件監聽範例
1 | // DOM 原生觀察者模式 |
FAQ:JavaScript 行為型設計模式常見問題
Q1:策略模式與狀態模式有什麼區別?
A:策略模式通常是由客戶端主動選擇演算法,且各策略間通常是獨立的;而狀態模式則是在物件內部根據狀態變化自動切換行為,且各狀態間通常存在切換邏輯。
Q2:為什麼在 React 中需要了解這些模式?
A:React 的 Hook(如 useReducer)與 Context API 的設計深受這些模式影響。了解命令模式有助於精進 Redux 開發,了解觀察者模式能幫助你理解為什麼狀態更新會觸發渲染,從而優化效能。
Q3:行為型模式會讓程式碼變複雜嗎?
A:在簡單的應用中可能會顯得過度設計(Over-engineering),但在中大型專案中,它們能有效防止程式碼變成難以維護的「義大利麵條(Spaghetti code)」,長期來看是降低複雜度的。
透過掌握這 5 大行為型設計模式,您將能更有系統地處理物件間的複雜互動。不論是透過 Strategy 優化邏輯切換,還是運用 Observer 建立響應式系統,這些技巧都將讓您的 JavaScript 程式碼更上一層樓!
喜歡這篇文章,請幫忙拍拍手喔 🤣
