首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

裝飾器模式實戰:繼承與聚合衝突的完美化解

     編輯:Java 程式設計入門

裝飾器模式旨在動態、組合式地擴展對象職責,避免繼承導致的類爆炸。但初學者常陷入「繼承抽象類 vs 聚合接口」的設計衝突。本文通過計數器案例,演示錯誤與正確實現對比,揭示「接口導向 + 聚合委託」的核心精髓。

案例需求

  • 基礎功能:Counter — 讀值、增、減
  • 擴展功能 1:上限控制(超過上限不再增)
  • 擴展功能 2:下限控制(低於下限不再減)
  • 要求:運行時動態組合,如「有上限無下限」、「有下限無上限」、「雙限制」

❌ 錯誤實現:繼承抽象類 + 聚合同類型

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;
    }
    // 覆寫方法...
}

致命缺陷

  1. 破壞 IS-A 原則:Decorator 不是 Counter 的子類型,只是包裝它
  2. 狀態雙份:父類 value 與聚合 counter.value 兩套狀態,同步極難
  3. 靜態綁定:繼承關係編譯期固定,無法運行時動態組合多層裝飾
  4. 構造器耦合:依賴 Counter(Counter) 複製構造器,違反封裝

✅ 正確實現:接口 + 聚合委託

步驟 1:定義核心接口

public interface Counter {
    int readValue();
    void increment();
    void decrement();
}

步驟 2:基礎實現

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--; }
}

步驟 3:抽象裝飾器(可選,消除樣板代碼)

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(); }
}

步驟 4:具體裝飾器

// 上限控制
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)));

總結

裝飾器模式的靈魂:接口定義契約,聚合實現委託,繼承僅用於復用委託樣板代碼(抽象裝飾器)。
拒絕繼承具體類,擁抱組合優於繼承,讓對象職責像洋蔥一層層包裹,剝開依然清晰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved