裝飾者(decorator,又稱 wrapper):把既有物件「包」一層、擴充其功能。
裝飾者提供與底層物件相似或相同的 API,方法內部則委派給底層物件。
裝飾者模式天生鼓勵跨層的 API 重複。
範例#
- Java I/O:
BufferedInputStream是InputStream的裝飾者- 介面相同,但加入緩衝
- 呼叫
read讀單一字元時,內部呼叫底層read讀一大塊,再把多餘字元留待後續呼叫使用
- 視窗系統:
ScrollableWindow裝飾Window,加上水平與垂直捲軸
設計動機與陷阱#
裝飾者的初衷:把專用擴充與通用核心分開。
但裝飾者類別往往很淺:為了一點點新功能,引入大量樣板程式碼,而且常含許多穿透型方法。
過度使用裝飾者,會像 Java I/O 一樣爆出大量淺類別。
建立 decorator 前的替代方案#
在建立 decorator 之前,先想想下列替代方案:
直接加進底層類別
- 適用情境:新功能本身相當通用、邏輯上與底層類別密切相關,或多數使用者反正都會用到
- 例如:幾乎每位
InputStream使用者都會建BufferedInputStream,緩衝又是 I/O 的自然部分 → 兩者就應該合併
與特定使用情境合併
- 若新功能是為某個特定情境特化的 → 不如併入該情境,不要做成獨立類別
與既有 decorator 合併
- 與其多開一個淺 decorator,不如把功能塞進現有 decorator → 變成一個更深的類別
獨立的 stand-alone 類別
- 問問自己:新功能真的需要包覆既有功能嗎?
- 例如視窗例子中,捲軸或許可以實作為獨立元件,不必包覆整個
Window
有時 decorator 確實合理——但通常有更好的替代方案。