裝飾器模式是一種極具彈性的結構型設計模式,它允許我們通過組合的方式動態擴展對象功能而無需修改原有結構。本文將通過JDK源碼中的實際應用和通俗易懂的代碼示例,帶你深入了解這一強大模式的精髓。
裝飾器模式核心原理
裝飾器模式的核心思想:在原有對象外面"包裝"一層新功能,同時保持與被裝飾對象相同的接口。它能夠:
- 在不改變對象的前提下增強功能
- 避免因過度繼承導致類爆炸
- 支持運行時動態添加功能
- 組合替代繼承提高靈活性
Java IO包中的裝飾器模式實戰
讓我們深入JDK源碼(Java 17),看看java.io
包如何完美應用裝飾器模式:
import java.io.*;public class DecoratorInJavaIO {public static void main(String[] args) throws IOException {// 基礎數據類型裝飾DataInputStream dataInput = new DataInputStream(new BufferedInputStream(new FileInputStream("data.bin")));// 字符編碼轉換裝飾BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("text.txt"), "UTF-8"));// 動態添加行號功能LineNumberReader lineReader = new LineNumberReader(reader);// 動態添加大小寫轉換裝飾器UpperCaseReader upperReader = new UpperCaseReader(lineReader);String line;while ((line = upperReader.readLine()) != null) {int num = upperReader.getLineNumber();System.out.println("Line " + num + ": " + line);}}
}// 自定義裝飾器:將內容轉為大寫
class UpperCaseReader extends FilterReader {protected UpperCaseReader(Reader in) {super(in);}@Overridepublic int read() throws IOException {int c = super.read();return (c == -1) ? c : Character.toUpperCase(c);}@Overridepublic int read(char[] cbuf, int off, int len) throws IOException {int n = super.read(cbuf, off, len);for (int i = off; i < off + n; i++) {cbuf[i] = Character.toUpperCase(cbuf[i]);}return n;}// 增強功能:提供讀取整行的方法public String readLine() throws IOException {char[] buffer = new char[1024];int pos = 0;int c;while ((c = read()) != -1) {if (c == '\n') break;buffer[pos++] = (char)c;}if (pos == 0 && c == -1) return null;return new String(buffer, 0, pos);}
}
在上述代碼中:
- 我們使用Java IO的核心裝飾器(
BufferedInputStream
,InputStreamReader
) - 展示了裝飾器鏈式組合的強大功能
- 創建了自定義的裝飾器
UpperCaseReader
來擴展原有功能
裝飾器模式結構解析
下面使用Mermaid工具展示裝飾器模式的類圖結構:
圖中關鍵角色:
- Component: 被裝飾對象的公共接口(如Java的InputStream)
- ConcreteComponent: 基礎實現(如FileInputStream)
- Decorator: 裝飾器抽象層(如FilterInputStream)
- ConcreteDecorator: 具體裝飾器實現(如BufferedInputStream)
JDK中裝飾器模式實現原理
分析java.io.FilterInputStream
源碼:
public class FilterInputStream extends InputStream {protected volatile InputStream in;protected FilterInputStream(InputStream in) {this.in = in;}public int read() throws IOException {return in.read();}// 所有方法都委托給in對象public int read(byte[] b) throws IOException {return read(b, 0, b.length);}public int read(byte[] b, int off, int len) throws IOException {return in.read(b, off, len);}// 其他方法...
}
在JDK實現中:
- 所有具體裝飾器都繼承自FilterInputStream
- 每個裝飾器持有底層InputStream的引用
- 基礎方法直接委托給底層流
- 需要增強的方法被重寫(如BufferedInputStream緩沖功能)
裝飾器模式 vs 繼承
特點 | 裝飾器模式 | 繼承 |
---|---|---|
擴展方式 | 運行時 | 編譯時 |
組合方式 | 對象組合 | 類繼承 |
靈活性 | 高(動態組合) | 低(靜態綁定) |
功能疊加 | 線性添加 | 只能單一路徑 |
修改風險 | 無(不修改原類) | 需要修改類層次 |
裝飾器模式的典型應用場景
- 輸入/輸出流處理:Java IO/NIO中的流裝飾
- Servlet API:HttpServletRequestWrapper裝飾請求
- Collections工具類:unmodifiableXXX創建不可變視圖
- JavaFX應用:Node對象的多種樣式裝飾
// Java集合框架中的裝飾器應用
List<String> origin = new ArrayList<>();
List<String> safeList = Collections.checkedList(origin, String.class);
List<String> unmodifiable = Collections.unmodifiableList(origin);
裝飾器模式的優點與局限
核心優勢:
- 符合開閉原則:擴展不修改
- 職責明確:小類單一職責
- 動態組合:運行時裝配功能
- 避免類爆炸:取代多層繼承結構
潛在缺點:
- 過度使用導致結構復雜
- 調試困難(調用鏈路深)
- 小對象數量可能增加
總結與最佳實踐
裝飾器模式在Java核心庫特別是IO系統中發揮了至關重要的作用。它通過優雅的包裝機制,實現了功能的動態組合,避免了傳統繼承的固有問題。
使用建議:
- 當需要動態、透明地添加職責時
- 當不適合使用子類擴展時
- 當目標可能有多種不同組合時
- 當需要保持被裝飾對象的接口純凈時
掌握裝飾器模式將使你的設計更具彈性,幫助創建更靈活、可擴展的系統架構。同時也要注意避免過度裝飾導致的復雜性,在恰當的場景發揮其最大價值。
設計思想的精髓: 組合優于繼承,封閉修改打開擴展,通過對象包裝而非類繼承來實現功能增強!