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

Decorator 模式實作陷阱:繼承與聚合衝突導致狀態不一致問題分析與解決

     編輯:Java 相關知識

前言:Decorator 模式核心概念

Decorator(裝飾者)模式動態為物件新增額外職責,猶如牆面刷油漆。相較於繼承產生大量子類別、編譯期靜態決定功能擴充,Decorator 提供「即插即用」的執行期動態組合彈性。

模式核心要素:

  1. 基本功能定義為介面
  2. Decorator 參數為介面,本身亦實作該介面以支援鏈式裝飾
  3. 基本功能類別實作介面,作為 Decorator 建構子參數
  4. 額外功能由 Decorator 內部資料結構處理

問題情境:計數器上下限控制實作

基礎 Counter 類別提供 increment()、decrement()、read_value() 方法。需動態新增:

  1. 上限控制 (UpperLimit)
  2. 下限控制 (LowerLimit)

初版實作中,Decorator 繼承 Counter 並聚合 Counter 參考:

class Decorator extends Counter {
    Counter counter;
    public Decorator(Counter c) {
        super(c);  // 父類別複製 counter.value
        counter = c;  // 自身聚合 counter 參考
    }
}

錯誤重現步驟與現象

  1. 選擇「Lower Limit」模式(工廠建立 new LowerLimit(new Counter(6)))
  2. 讀取數值 → 6(正確)
  3. 執行 Increment → 數值應為 7
  4. 讀取數值 → 7(正確)
  5. 執行 Decrement → 數值應回 6
  6. 讀取數值 → 仍為 7(錯誤!)

UpperLimit 同樣出現此問題。

根因深度追蹤

觀察 LowerLimit.decrement() 開頭兩行追蹤輸出:

Class value : 7   // 呼叫父類別繼承而來的 read_value()
Dec value : 6     // 呼叫聚合物件 counter.read_value()

存在兩份 value:

  1. 父類別 Counter 複製而來的 value(位於 Decorator 繼承體系中)
  2. 聚合成員變數 counter 參考的原始物件 value

產生原因:工廠傳入匿名 Counter 物件 → Decorator 建構子先呼叫 super(c) 讓父類別複製 value → 再將參考存入自身 counter 成員。導致同一邏輯實體擁有兩份獨立狀態。

LowerLimit 僅覆寫 decrement(),呼叫 read_value() 與 increment() 時走父類別邏輯(操作第一份 value);呼叫 decrement() 時走自身邏輯(操作第二份 value)。雙軌並行造成狀態不同步。

解決方案:徹底委派、拒絕繼承狀態

Decorator 模式本意為「避免繼承、採用委派」。解決核心在於:

  1. 覆寫所有公開方法,內部完全委派給聚合物件 counter
  2. 移除繼承自父類的狀態(不呼叫 super(c) 複製數值)
  3. 將程式碼中被註解的方法(read_value()、increment() 等委派實作)啟用

修正後的 UpperLimit 範例:

class UpperLimit extends Decorator {
    public UpperLimit(Counter c) { super(c); }
    
    public void increment() {
        if (counter.read_value() > 20) {
            System.out.println("Too High");
        } else {
            counter.increment();
        }
    }
    public void decrement() { counter.decrement(); }
    public int read_value() { return counter.read_value(); }
}

總結與設計原則

Decorator 建構子參數代表「既有類別基礎」,自身聚合物件必須持有該參數參考,所有方法必須重寫委派,否則將陷入「繼承陷阱」:父類別操作複製的狀態、子類別操作聚合物件的狀態,雙軌並行導致狀態分歧。

此案例深刻說明:設計模式不僅是結構圖,更蘊含執行期行為契約。Decorator 模式要求「介面繼承 + 物件組合 + 完全委派」,任何保留繼承實作細節的做法皆違背模式初衷,必生 Bug。

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