動態地給對象添加職責,比生成子類更靈活。關鍵:被裝飾者與裝飾者共享同一接口/父類,客戶端無感知;裝飾者持有被裝飾者引用,轉發請求並在前後執行額外邏輯。
Component (java.io.OutputStream) ← 抽象父類,定義 write(int b)
│
├─ ConcreteComponent: FileOutputStream, ByteArrayOutputStream, PipedOutputStream
│
└─ Decorator (java.io.FilterOutputStream) ← 抽象裝飾基類,持有 out 引用
│
├─ BufferedOutputStream (緩衝)
├─ DataOutputStream (基本類型寫入)
├─ PrintStream (print/println、自動flush、異常吞沒)
├─ ObjectOutputStream (對象序列化)
├─ CipherOutputStream (加密)
├─ GZIPOutputStream (壓縮)
└─ ... (用戶自定義)
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException; // 核心抽象方法
public void write(byte[] b) throws IOException { write(b, 0, b.length); }
public void write(byte[] b, int off, int len) throws IOException {
for (int i = 0; i < len; i++) write(b[off + i]); // 默認逐字節寫入 (子類通常覆寫優化)
}
public void flush() throws IOException {}
public void close() throws IOException {}
}
public class FileOutputStream extends OutputStream {
private final FileDescriptor fd;
// native 方法直接調用 OS write
private native void write(int b) throws IOException;
private native void write(byte[] b, int off, int len) throws IOException;
// ... 文件打開/關閉/獲取 Channel 等
}
public class FilterOutputStream extends OutputStream {
protected final OutputStream out; // 持有被裝飾者引用
protected FilterOutputStream(OutputStream out) { this.out = out; }
// 默認轉發實現,子類可選擇性覆寫
public void write(int b) throws IOException { out.write(b); }
public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); }
public void flush() throws IOException { out.flush(); }
public void close() throws IOException { out.close(); }
}
public class DataOutputStream extends FilterOutputStream implements DataOutput {
public DataOutputStream(OutputStream out) { super(out); }
// 新增業務方法:寫入基本類型
public final void writeInt(int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write(v & 0xFF);
}
public final void writeUTF(String str) throws IOException { /* Modified UTF-8 編碼 */ }
// ... writeBoolean, writeLong, writeDouble 等
// 繼承 write(byte[]) 等轉發方法,無需覆寫
}
// 寫入文件、緩衝、基本類型、對象序列化
OutputStream out = new ObjectOutputStream(
new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("data.bin"))));
out.writeInt(42);
out.writeUTF("Hello");
out.writeObject(new User("Alice"));
out.close(); // 關閉鏈路自動級聯關閉底層流
public class CountingOutputStream extends FilterOutputStream {
private long bytesWritten = 0;
public CountingOutputStream(OutputStream out) { super(out); }
@Override
public void write(int b) throws IOException {
out.write(b);
bytesWritten++;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
bytesWritten += len;
}
public long getBytesWritten() { return bytesWritten; }
public void resetCount() { bytesWritten = 0; }
}
// 使用
CountingOutputStream counter = new CountingOutputStream(new FileOutputStream("log.txt"));
counter.write("Hello".getBytes());
System.out.println("Written: " + counter.getBytesWritten() + " bytes"); // 5
| 維度 | 裝飾器 | 繼承 | 代理 |
|---|---|---|---|
| 關系 | 組合 (Has-A) | 繼承 (Is-A) | 組合 (Has-A) |
| 靈活性 | 運行時動態組合 | 編譯期靜態 | 運行時可換 |
| 職責 | 增強/添加功能 | 特化/覆寫 | 控制訪問/遠程/延遲 |
| 透明性 | 完全透明 (同接口) | 透明 (是子類) | 透明 (同接口) |
| 典型場景 | IO 流、集合包裝、Servlet Filter | 模板方法、框架擴展點 | 遠程調用、AOP、懶加載 |
java.nio.channels.AsynchronousFileChannel + CompletionHandler:異步非阻塞 IO,無裝飾器鏈路。java.util.stream.Stream / reactor.core.publisher.Flux:聲明式數據流管道,操作符鏈替代裝飾器嵌套。掌握 IO 裝飾器源碼結構,能讀懂所有基於流/鏈路/管道的中間件架構。