當實作中的變化點未被封裝在抽象或階層內時,就出現 Missing Encapsulation 壞味道。
核心定義#
兩種典型表現:
- 客戶端緊耦合於它所需服務的多個變體——每多支援一個變體或修改既有變體,客戶端都跟著痛。
- 階層中為每一種「變化組合」都建一個類別——一旦新增維度,類別爆炸。
此壞味道直接違反 Open Closed Principle ↗(OCP):型別應「對擴充開放、對修改關閉」。
案例:ResourceBundle 的 OCP#
JDK 7 的
java.util.ResourceBundle預設實作足以應付多數情境,但對於需要自訂搜尋策略、快取機制的應用,提供了ResourceBundle.Control作為擴充點。
這是「simple things should be simple, complex things should be possible」(Alan Kay)的具體實踐——也是封裝原則中隱藏變化(Variation Encapsulation Principle ↗)的好例子。
常見成因#
- 不察覺哪些關注點會變:缺乏 OCP 訓練、缺乏對產品 roadmap 的判斷。
- 缺乏重構:需求變動時未即時調整設計。
- 混合關注點:多個正交(orthogonal)關注點塞進同一個階層。
- 天真的設計決策:「每個變化組合都建一個類別」——典型的反樣式。
範例#
範例 1:Encryption 類別硬塞多種演算法#

Figure 4.9: Encryption 類別最初僅提供 DES 演算法
最初 Encryption 類別只支援 DES。為了支援 AES,新手把 encryptUsingDES()、encryptUsingAES() 都加進 Encryption:
後果:
Encryption類別越來越大、越難維護。- 新加演算法必須改
Encryption本身——違反 OCP。- 演算法被鎖死在
Encryption內,無法在其他脈絡中重用。

Figure 4.10: Encryption 類別同時提供 DES 與 AES
範例 2:內容 × 演算法的類別爆炸#
設計支援多種內容(image、text)× 多種演算法(DES、AES)的加密——若為每個組合建一個類別(DESImageEncryption、AESTextEncryption…),新增 TDES 與 Data 內容類別時:

Figure 4.11: 支援多種內容 × 演算法加密的階層
flowchart TD
A[Encryption] --> B[DESImage]
A --> C[DESText]
A --> D[AESImage]
A --> E[AESText]
A --> F[TDESImage]
A --> G[TDESText]
A --> H[DESData]
A --> I[AESData]
A --> J[TDESData]兩個變化點 × N 種選項 = N² 個類別。維度多了就是組合爆炸。

Figure 4.12: Encryption 階層中的類別爆炸
建議重構#
通用解:封裝變化。常用樣式:Strategy ↗、Bridge ↗。
範例 1:套用 Strategy 樣式#
- 建立
EncryptionAlgorithm介面。 DESEncryptionAlgorithm、AESEncryptionAlgorithm各實作其演算法。Encryption類別持有一個EncryptionAlgorithm參考。
優點:
- 執行期可切換演算法。
- 演算法可在其他脈絡中重用。
- 新增演算法不必動
Encryption。

Figure 4.15: 套用 Strategy 樣式的建議重構
書中也分析過用「繼承」做的兩種錯解——其一造成 Broken Hierarchy(6.8 節),其二讓子類別繼承不必要的方法、降低重用性。

Figure 4.13: 重構選項一(Broken Hierarchy)

Figure 4.14: 重構選項二(使用繼承)
範例 2:套用 Bridge 樣式#
把「內容類型」與「演算法類型」拆成兩個獨立的階層,由 EncryptionContentType 階層持有 EncryptionAlgorithm 介面參考。

Figure 4.16: 支援多種內容與演算法的建議重構
新增 TDES 與 Data 時:原本要加 5 個類別(爆炸版),重構後只需 2 個類別。
對品質屬性的衝擊#
- 理解性:抽象內塞了多種服務 → 變複雜;類別爆炸 → 整體結構難理解。
- 可變更性與可擴充性:客戶端緊耦合到所有變體。
- 可重用性:服務嵌在抽象中無法獨立使用;正交關注點混雜時更難拆出可用的單元。
別名#
- Nested generalization:階層第一層做了一個泛化,後續層則「乘出」所有組合。
- Class explosion:因多重泛化導致類別爆炸。
- Combinatorial explosion:每新增一族變體就要再加一群類別。