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

裝飾模式在 Java 中的三種實作策略:繼承、封裝與外掛

     編輯:Java 程式設計入門

什麼是裝飾模式?

Decorator(裝飾者)用於動態擴展物件職責,不改變被裝飾物件本身屬性。生活例子:相框裝飾畫作、化妝品裝飾臉龐。Java 經典案例:

  • JScrollPane 裝飾 JComponent → 新增可捲動屬性。
  • BufferedInputStream 裝飾 InputStream → 新增緩衝讀取加速。
  • DebugButton 裝飾 JButton → 點擊時記錄日誌。
  • ScrollOverButton 裝飾 JButton → 滑鼠經過顯示凸起邊框。

三種實作模型比較

1. 繼承

public class DebugButton extends JButton {
    public DebugButton() {
        addActionListener(e -> System.out.println("debug message"));
    }
}

// ScrollOverButton 同理,加入 MouseListener 切換邊框
public class ScrollOverButton extends JButton {
    public ScrollOverButton() {
        addMouseListener(new MouseAdapter() {
            @Override public void mouseEntered(MouseEvent e) { setBorder(BorderFactory.createRaisedBevelBorder()); }
            @Override public void mouseExited(MouseEvent e) { setBorder(BorderFactory.createEmptyBorder()); }
        });
    }
}

優點:直觀、保留原類型(DebugButton instanceof JButton 為 true)、幾乎能實作所有裝飾。

缺點:

  1. 組合爆炸:需同時擁有 Debug 與 ScrollOver 行為時,必須再寫 ScrollOverDebugButton;若有 N 種行為,理論上需 2^N 個子類別。
  2. 開放/封閉原則違背:新增行為需修改裝飾類別代碼(加開關方法 setDebug()/isDebug() 等)。
  3. 視覺屬性耦合:Look & Feel 變更可能導致繼承實作失效。
  4. 建構子重載繁瑣:為保留原類型需重載所有建構子。

2. 封裝(Wrapper / 組合)

核心思想:持有被裝飾物件參考,轉發請求並在前後加入新行為。

// BufferedInputStream 經典封裝實作(簡化)
public class BufferedInputStream extends InputStream {
    private InputStream in; // 被裝飾物件
    private byte[] buf;     // 緩衝區
    public BufferedInputStream(InputStream in) { this.in = in; }
    @Override public int read() throws IOException {
        // 先讀緩衝區,空則 in.read() 填充
    }
}

// JScrollPane 封裝視圖組件
// DebugButton 封裝實作
public class DebugButton extends JButton { // 仍繼承 JButton 以保型別
    private JButton delegate;
    public DebugButton(JButton delegate) { this.delegate = delegate; }
    // 轉發所有方法給 delegate,並在 actionPerformed 前加日誌
}

優點:執行期動態組合(new DebugButton(new ScrollOverButton(new JButton())))、符合開放/封閉原則、無組合爆炸。

缺點:型別身分喪失——JScrollPane 不能替換內部視圖組件(getFont() 返回自身字體而非視圖字體),instanceof 檢查失效。

3. 外掛(External / AOP 風格)

將裝飾邏輯完全外部化,透過事件監聽、屬性映射、動態代理或 AOP 框架(Spring AOP、AspectJ)在不修改原類別、不建立子類別情況下注入行為。例如:

  • 為所有 JButton 註冊全域 ActionListener 記錄日誌。
  • 用 UIManager 設定全域 ButtonUI 實現滑鼠經過效果。
  • Spring AOP 切面攔截 Service 方法加入事務、快取、日誌。

優點:零侵入、關注點分離、大範圍統一套用。

缺點:需框架支援、除錯較難、效能微額開銷。

選型建議

場景推薦策略
少數固定裝飾、需保留型別繼承
動態組合、行為正交、不需 instanceof封裝
橫切關注點(日誌、事務、安全)、大量同類元件外掛/AOP

Java I/O 庫(BufferedInputStream、GZIPInputStream)採封裝;Swing 邊框/UI 委派採外掛;簡單 UI 擴展常用繼承。實務中常混合使用。

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