🎯 什麼情境該想到我
當你「知道該用組合、該抽介面,但不知道該抽哪裡」的時候。
★ 這是所有設計模式背後的那一條原則——GoF 二十三個模式都是它的具體化。
而它的難處不在「怎麼封裝」,在**「找對變化點」**。
⚙️ 怎麼用
原則
找出會變動的部分,把它與不變的部分分開,並封裝起來。
會變的 → 抽成可替換的元件(介面 + 實作)
不變的 → 保持簡單,依賴抽象而非具體
GoF 二十三個模式其實都在回答「封裝什麼」:
| 封裝什麼 | 模式 |
|---|---|
| 演算法 | 策略模式 |
| 物件的建立 | 工廠方法模式、抽象工廠模式、建造者模式 |
| 額外的行為 | 裝飾者模式 |
| 狀態相依的行為 | 狀態模式 |
| 對結構的操作 | 訪問者模式 |
| 請求本身 | 命令模式 |
| 物件之間的互動 | 中介者模式 |
| 走訪方式 | 迭代器模式 |
| 兩個獨立變化的維度 | 橋接模式 |
★ 記住這張表比背二十三個模式有用:
遇到問題時問「這裡會變的是什麼?」,答案會直接指向對應的模式。
★ 怎麼找出變化點(這才是難的部分)
不要用猜的。用證據:
| 證據來源 | 怎麼看 |
|---|---|
| ★ git 歷史 | git log --format=format: --name-only | sort | uniq -c | sort -rn | head -20★ 過去一年改最多次的檔案,就是你的變化點——這是最可靠的證據 |
| 需求的措辭 | 「目前支援 A,未來還會有 B、C」← 明確的變化軸 |
| ★ 條件分支的成長 | 同一個 switch 在三次 commit 裡各加了一個 case → 它就是變化點 |
| 重複的形狀 | 三個函式長得幾乎一樣,只差中間幾行 |
| 領域知識 | 稅率、匯率、法規、費率本來就會變;資料結構的核心運算通常不會 |
★
git log那一招值得記起來。 它把「我覺得這裡會變」變成「這裡過去一年改了 47 次」。
(見 工具-影響分析、工具-破窗理論與軟體熵)
★ 一個具體的判斷流程
① 這個需求變動,需要改幾個地方?
1 個 → 沒問題,不需要封裝
多個且分散 → ★ 有變化點沒被封裝
② 這些地方為什麼會一起改?
→ 它們共享的那個「概念」就是該被封裝的東西
③ 把那個概念抽出來,讓其他部分依賴它的介面
反例(抽錯地方):
現象:每加一種報表就要改三個檔案
❌ 誤判:「報表產生器」是變化點 → 抽一個 ReportGenerator 介面
結果:三個檔案還是都要改,因為真正變的是「資料來源 × 輸出格式」兩個維度
✅ 正解:兩個維度各自封裝(見 [[橋接模式]])
🧪 我實際套用的紀錄
- 2026-07-14:(待填)
⚠️ 注意 / 什麼時候不適用
- ★ 猜錯變化點比不封裝更糟。你付了抽象的代價(多一層間接、更難讀),
卻沒有得到彈性——而且錯誤的抽象會抵抗正確的改動。 - ★ 等證據出現再封裝。第一次寫死、第二次容忍重複、第三次才抽象。
Sandi Metz:“duplication is far cheaper than the wrong abstraction.”(見 工具-DRY原則) - 不是所有變動都該被封裝。有些改動就是該改程式碼——
封裝的目的是「讓高頻改動變安全」,不是「讓所有改動都不用改程式」。 - 封裝有成本:多一個介面、多一層間接、多一個「誰決定用哪個」的問題、profiling 變難。
變化頻率低的地方不值得付。 - ★ 過度封裝的症狀:每個 struct 都有一個同名介面、追一個呼叫要跳五層、
改一行要看三個檔案——這時候「彈性」已經變成「難以理解」。 - 維度要分清楚。兩個獨立變化的維度硬塞進一個抽象 → 類別爆炸(見 橋接模式)。