裝飾器模式旨在動態、組合式地擴展對象職責,避免繼承導致的類爆炸。但初學者常陷入「繼承抽象類 vs 聚合接口」的設計衝突。本文通過計數器案例,演示錯誤與正確實現對比,揭示「接口導向 + 聚合委託」的核心精髓。
Counter — 讀值、增、減class Counter { // 具體類
private int value;
public Counter(int v) { value = v; }
public Counter(Counter cc) { value = cc.value; } // 複製構造器
public int readValue() { return value; }
public void increment() { value++; }
public void decrement() { value--; }
}
class Decorator extends Counter { // 錯誤:繼承具體類
Counter counter; // 聚合同類型
public Decorator(Counter c) {
super(c); // 調用父類複製構造器
this.counter = c;
}
// 覆寫方法...
}
Decorator 不是 Counter 的子類型,只是包裝它value 與聚合 counter.value 兩套狀態,同步極難Counter(Counter) 複製構造器,違反封裝public interface Counter {
int readValue();
void increment();
void decrement();
}
public class SimpleCounter implements Counter {
private int value;
public SimpleCounter(int initialValue) { this.value = initialValue; }
@Override public int readValue() { return value; }
@Override public void increment() { value++; }
@Override public void decrement() { value--; }
}
public abstract class CounterDecorator implements Counter {
protected final Counter delegate;
public CounterDecorator(Counter delegate) { this.delegate = delegate; }
@Override public int readValue() { return delegate.readValue(); }
@Override public void increment() { delegate.increment(); }
@Override public void decrement() { delegate.decrement(); }
}
// 上限控制
public class UpperBoundCounter extends CounterDecorator {
private final int upperBound;
public UpperBoundCounter(Counter delegate, int upperBound) {
super(delegate);
this.upperBound = upperBound;
}
@Override
public void increment() {
if (delegate.readValue() < upperBound) {
delegate.increment();
}
}
}
// 下限控制
public class LowerBoundCounter extends CounterDecorator {
private final int lowerBound;
public LowerBoundCounter(Counter delegate, int lowerBound) {
super(delegate);
this.lowerBound = lowerBound;
}
@Override
public void decrement() {
if (delegate.readValue() > lowerBound) {
delegate.decrement();
}
}
}
public class CounterDemo {
public static void main(String[] args) {
// 基礎計數器:初始值 0
Counter counter = new SimpleCounter(0);
// 動態添加上限 10
counter = new UpperBoundCounter(counter, 10);
// 再動態添加下限 -5
counter = new LowerBoundCounter(counter, -5);
// 測試
for (int i = 0; i < 15; i++) counter.increment();
System.out.println("增 15 次後: " + counter.readValue()); // 10 (觸及上限)
for (int i = 0; i < 20; i++) counter.decrement();
System.out.println("減 20 次後: " + counter.readValue()); // -5 (觸及下限)
}
}
| 維度 | 錯誤:繼承具體類 | 正確:接口 + 聚合 |
|---|---|---|
| 類型關係 | IS-A (虛假) | HAS-A (委託) |
| 狀態管理 | 雙份、易不同步 | 單一源頭、委託鏈 |
| 組合靈活性 | 編譯期固定、單層 | 運行時任意層、任意順序 |
| 擴展新功能 | 修改父類或新增子類 | 新增裝飾器類,零修改現有代碼 |
| 符合原則 | 違反 LSP、OCP | 滿足 LSP、OCP、SRP、DIP |
Java 8+ 可用 Function 替代裝飾器類:
Function withUpperBound = c -> new UpperBoundCounter(c, 10);
Function withLowerBound = c -> new LowerBoundCounter(c, -5);
Counter counter = withLowerBound.apply(withUpperBound.apply(new SimpleCounter(0)));
裝飾器模式的靈魂:接口定義契約,聚合實現委託,繼承僅用於復用委託樣板代碼(抽象裝飾器)。
拒絕繼承具體類,擁抱組合優於繼承,讓對象職責像洋蔥一層層包裹,剝開依然清晰。