當你需要在不修改原有類、不使用繼承的前提下,為物件「動態地、透明地」增加職責時,裝飾模式是教科書級的解決方案。它以組合取代繼承,實現「開放擴展、關閉修改」的開閉原則精髓。

| 角色 | 類別/介面 | 職責 |
|---|---|---|
| Component | 介面或抽象類 | 定義被裝飾物件的核心契約 (如 doSomething()) |
| ConcreteComponent | 實作類 | 最基礎、最核心的實作 (如 ConcreteComponent) |
| Decorator | 抽象類 | 持有 Component 參考,委派核心行為,定義擴展點 |
| ConcreteDecorator | 具體裝飾類 | 在委派前/後添加額外邏輯 (如 ConcreteDecoratorA) |
// 1. 抽象構件
public abstract class Component {
public abstract void doSomething();
}
// 2. 具體構件
public class ConcreteComponent extends Component {
@Override public void doSomething() {
System.out.println("核心業務邏輯");
}
}
// 3. 抽象裝飾者 (關鍵:持有 Component 參考)
public abstract class Decorator extends Component {
protected Component component;
public Decorator(Component component) { this.component = component; }
@Override public void doSomething() { component.doSomething(); } // 預設委派
}
// 4. 具體裝飾者 A:前置增強
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component c) { super(c); }
@Override public void doSomething() {
before(); // 新增:前置邏輯
super.doSomething(); // 委派核心
}
private void before() { System.out.println("DecoratorA: 前置處理"); }
}
// 5. 具體裝飾者 B:後置增強
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component c) { super(c); }
@Override public void doSomething() {
super.doSomething(); // 委派核心
after(); // 新增:後置邏輯
}
private void after() { System.out.println("DecoratorB: 後置處理"); }
}
// 6. 客戶端:像剝洋蔥一樣層層包裹
public class Client {
public static void main(String[] args) {
Component core = new ConcreteComponent();
core = new ConcreteDecoratorA(core); // 第一層
core = new ConcreteDecoratorB(core); // 第二層
core.doSomething(); // 執行完整鏈路
}
}
// 輸出:
// DecoratorA: 前置處理
// 核心業務邏輯
// DecoratorB: 後置處理
場景:基礎訂單需疊加「滿減」、「優惠券」、「會員折扣」、「首單禮」等促銷規則,規則頻繁變動、組合多變。

// Component: 訂單核心介面
public interface Order {
BigDecimal calculate(); // 計算最終金額
String getDetail(); // 明細描述
}
// ConcreteComponent: 基礎訂單
public class BaseOrder implements Order {
private BigDecimal amount;
public BaseOrder(BigDecimal amount) { this.amount = amount; }
public BigDecimal calculate() { return amount; }
public String getDetail() { return "基礎金額: " + amount; }
}
// Decorator: 抽象促銷裝飾者
public abstract class PromotionDecorator implements Order {
protected Order order;
public PromotionDecorator(Order order) { this.order = order; }
public BigDecimal calculate() { return order.calculate(); }
public String getDetail() { return order.getDetail(); }
}
// ConcreteDecorators: 具體促銷規則
public class FullReductionDecorator extends PromotionDecorator {
private BigDecimal threshold, reduction;
public FullReductionDecorator(Order o, BigDecimal t, BigDecimal r) { super(o); threshold=t; reduction=r; }
@Override public BigDecimal calculate() {
BigDecimal amt = super.calculate();
return amt.compareTo(threshold) >= 0 ? amt.subtract(reduction) : amt;
}
@Override public String getDetail() { return super.getDetail() + " | 滿減: " + reduction; }
}
public class CouponDecorator extends PromotionDecorator {
private BigDecimal couponValue;
public CouponDecorator(Order o, BigDecimal v) { super(o); couponValue=v; }
@Override public BigDecimal calculate() { return super.calculate().subtract(couponValue); }
@Override public String getDetail() { return super.getDetail() + " | 優惠券: " + couponValue; }
}
public class MemberDiscountDecorator extends PromotionDecorator {
private BigDecimal rate; // 0.9 = 9 折
public MemberDiscountDecorator(Order o, BigDecimal r) { super(o); rate=r; }
@Override public BigDecimal calculate() { return super.calculate().multiply(rate).setScale(2, RoundingMode.HALF_UP); }
@Override public String getDetail() { return super.getDetail() + " | 會員折扣: " + rate; }
}
// Client: 動態組合促銷策略
public class OrderService {
public Order buildPromotedOrder(Order base, User user, List coupons) {
Order decorated = base;
// 1. 滿減
decorated = new FullReductionDecorator(decorated, new BigDecimal("200"), new BigDecimal("30"));
// 2. 優惠券
if (!coupons.isEmpty()) decorated = new CouponDecorator(decorated, coupons.get(0).getValue());
// 3. 會員折扣
if (user.isVip()) decorated = new MemberDiscountDecorator(decorated, new BigDecimal("0.9"));
return decorated;
}
}
| 優點 | 缺點 / 注意點 |
|---|---|
| 裝飾類與被裝飾類解耦,單一職責 | 過多裝飾層導致除錯困難、堆疊順序敏感 |
| 替代繼承爆炸,支援動態組合 | 身份判斷失效:decorated instanceof ConcreteComponent 為 false |
| 符合開閉原則,擴展不修改核心 | 若頻繁新增裝飾類,考慮責任鏈或策略模式替代 |
可疊加多層,還原 is-a 關係 | 序列化/反序列化需特殊處理包裹鏈 |
BufferedReader 裝飾 FileReader:增加緩衝讀取能力GZIPInputStream 裝飾 FileInputStream:增加解壓縮能力Collections.synchronizedList() 裝飾 ArrayList:增加線程安全能力Collections.unmodifiableList():增加唯讀保護能力這些經典案例證明:裝飾模式不是理論玩具,而是 Java 標準庫處理「橫切能力增強」的基石模式。
裝飾模式以「包裹 + 委派 + 增強」三步曲,優雅解決動態職責疊加難題。下次當你面臨「如果加上 A 功能、再加上 B 功能、順序還可能互換」的需求時,請優先考慮裝飾模式——它能讓核心業務邏輯保持純粹,而將變化點封裝在獨立、可組合的裝飾類中。