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

Java 裝飾器模式的三種實現方式深度解析

     編輯:Java 相關知識

前言:從生活實例理解裝飾器模式

凡研讀過 GoF《設計模式》一書的開發者,無不熟知 Decorator(裝飾器)模式。不妨暫時拋開既有認知,從日常開發經驗出發重新審視:裝飾器本質上是用於「裝飾某事物」的另一事物,它能直接改變被裝飾對象的職責或特徵,卻不改變其固有屬性。如相框裝飾畫作、化妝品修飾臉龐。

專業視角下的經典案例

  • JScrollPane:裝飾 JComponent 視圖區,賦予可滾動能力,元件本身不變。
  • BufferedInputStream:裝飾 InputStream,提供緩衝讀取,提升速度,本身仍是 InputStream。
  • DebugButton:行為同 JButton,點擊時額外寫入日誌,屬性不變。
  • ScrollOverButton:滑鼠經過時呈現凸起邊框,移出則平坦,同屬 JButton 裝飾器。

三大實現模型

裝飾器主要有三種實現路徑:繼承、封裝、外掛。以下逐一剖析優劣與適用場景。

一、繼承實現

最直觀的方式:寫一個衍生類別繼承被裝飾類別,新增或覆寫方法賦予新職責。

public class DebugButton extends JButton {
    public DebugButton() {
        addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("debug message");
            }
        });
    }
}

ScrollOverButton 同理,改掛 MouseListener 動態切換邊框。BufferedInputStream 覆寫 read() 從緩衝區讀取。但 JScrollPane 若用繼承實現則極為複雜。

優點

  1. 幾乎能實現所有裝飾器。
  2. 保留被裝飾類別原始型別,可直接替換使用(如以 ScrollOverButton 取代 JButton)。

缺點

  1. 擴展性差:需組合多種行為時,只能不斷衍生新類別(如 ScrollOverDebugButton),導致類別爆炸,違背開放封閉原則。
  2. 樣式變動難應對:視覺元件行為常由可插拔 Look&Feel 決定,繼承方式需修改實作碼才能適應。
  3. 建構子重載繁瑣:為保持原始型別,必須重載所有建構子,甚至靜態工廠,維護成本高。

結論:繼承方式看似簡單,實則擴展困難、違背 OO 原則,不建議用於實現裝飾器。

二、封裝實現

核心思想:將被裝飾物件包裝在裝飾器內部,請求轉發給內部物件,轉發前後執行額外邏輯。

經典案例重寫

  • BufferedInputStream:建構子接收 InputStream 實例,轉發請求並加入緩衝邏輯。
  • JScrollPane:轉發請求給內部 View 元件,但無法完全替換 View(如 getFont() 回傳自身字型)。
  • DebugButton:若繼承 JButton 需轉發約 180 個方法;若改繼承 JComponent 則失去 JButton 型別,JScrollPane 採此法。

優點

  1. 程式碼精簡(適用方法較少的類別,如 InputStream)。
  2. 各裝飾器獨立,可同時疊加多個裝飾器。

缺點

  1. 對方法眾多的視覺類別,需撰寫大量轉發方法,或犧牲型別換取簡潔。
  2. GoF 指出封裝才是真正裝飾器,但長類別迫使開發者在「型別保留」與「程式碼簡潔」間二選一。

三、外掛實現

不建立新類別,而是利用被裝飾物件既有的回調機制(如 Listener)動態掛載行為。

DebugDecorator 範例

public class DebugDecorator implements ActionListener {
    public void decorateDebug(JButton butt) { butt.addActionListener(this); }
    public void undecorateDebug(JButton butt) { butt.removeActionListener(this); }
    public void actionPerformed(ActionEvent evt) {
        JButton src = (JButton)evt.getSource();
        System.out.println("debug message for button " + src);
    }
}

使用時:

DebugDecorator decor = new DebugDecorator();
decor.decorateDebug(myButt);   // 加裝
decor.undecorateDebug(myButt); // 卸載

同樣可實現 RollOverDecorator,兩者互不干擾,隨時增減。更可寫成單例(Singleton Decorator)供全系統共用,並擴充 decorateDebugContainer() 批量套用至容器內所有按鈕。

優點

  1. 完全不改變被裝飾物件型別。
  2. 裝飾器獨立、可動態增減、無需修改既有代碼,完美適應視覺元件多變的 Look&Feel。

缺點

  1. 依賴被裝飾物件提供回調、邊框、佈局管理等特定特徵,無此特徵則無法使用(如 BufferedInputStream 無對應 Listener)。
  2. 需修改 paint() 等核心繪製邏輯時(如圓角按鈕)亦無能為力。

總結比較

實現方式優勢劣勢建議
繼承保留型別、直觀擴展性差、類別爆炸、違背 OO不推薦
封裝符合 GoF 定義、短類別適用長類別需大量轉發或犧牲型別適用方法較少的類別(如 Stream)
外掛動態、零侵入、符合 OO、視覺元件效果佳受限於被裝飾物件特徵、無法改核心繪製首選視覺元件裝飾,視情況輔以封裝
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved