JDK為程序員提供了大量的類庫,而為了保持類庫的可重用性,可擴展性和靈活性,其中使用到了大量的設計模式,本文將介紹JDK的I/O包中使用到的Decorator模式,並運用此模式,實現一個新的輸出流類。
Decorator模式簡介
Decorator模式又名包裝器(Wrapper),它的主要用途在於給一個對象動態的添加一些額外的職責。與生成子類相比,它更具有靈活性。
有時候,我們需要為一個對象而不是整個類添加一些新的功能,比如,給一個文本區添加一個滾動條的功能。我們可以使用繼承機制來實現這一功能,但是這種方法不夠靈活,我們無法控制文本區加滾動條的方式和時機。而且當文本區需要添加更多的功能時,比如邊框等,需要創建新的類,而當需要組合使用這些功能時無疑將會引起類的爆炸。
我們可以使用一種更為靈活的方法,就是把文本區嵌入到滾動條中。而這個滾動條的類就相當於對文本區的一個裝飾。這個裝飾(滾動條)必須與被裝飾的組件(文本區)繼承自同一個接口,這樣,用戶就不必關心裝飾的實現,因為這對他們來說是透明的。裝飾會將用戶的請求轉發給相應的組件(即調用相關的方法),並可能在轉發的前後做一些額外的動作(如添加滾動條)。通過這種方法,我們可以根據組合對文本區嵌套不同的裝飾,從而添加任意多的功能。這種動態的對對象添加功能的方法不會引起類的爆炸,也具有了更多的靈活性。
以上的方法就是Decorator模式,它通過給對象添加裝飾來動態的添加新的功能。如下是Decorator模式的UML圖:
Component為組件和裝飾的公共父類,它定義了子類必須實現的方法。
ConcreteComponent是一個具體的組件類,可以通過給它添加裝飾來增加新的功能。
Decorator是所有裝飾的公共父類,它定義了所有裝飾必須實現的方法,同時,它還保存了一個對於Component的引用,以便將用戶的請求轉發給Component,並可能在轉發請求前後執行一些附加的動作。
ConcreteDecoratorA和ConcreteDecoratorB是具體的裝飾,可以使用它們來裝飾具體的Component。
Java IO包中的Decorator模式
JDK提供的java.io包中使用了Decorator模式來實現對各種輸入輸出流的封裝。以下將以java.io.OutputStream及其子類為例,討論一下Decorator模式在IO中的使用。
首先來看一段用來創建IO流的代碼:
以下是代碼片段:
try {
OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
這段代碼對於使用過JAVA輸入輸出流的人來說再熟悉不過了,我們使用DataOutputStream封裝了一個FileOutputStream。這是一個典型的Decorator模式的使用,FileOutputStream相當於Component,DataOutputStream就是一個Decorator。將代碼改成如下,將會更容易理解:
以下是代碼片段:
try {
OutputStream out = new FileOutputStream("test.txt");
out = new DataOutputStream(out);
} catch(FileNotFoundException e) {
e.printStatckTrace();
}
由於FileOutputStream和DataOutputStream有公共的父類OutputStream,因此對對象的裝飾對於用戶來說幾乎是透明的。下面就來看看OutputStream及其子類是如何構成Decorator模式的:
OutputStream是一個抽象類,它是所有輸出流的公共父類,其源代碼如下:
以下是代碼片段:
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException;
...
}
它定義了write(int b)的抽象方法。這相當於Decorator模式中的Component類。
ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三個類都直接從OutputStream繼承,以ByteArrayOutputStream為例:
以下是代碼片段:
public class ByteArrayOutputStream extends OutputStream {
protected byte buf[];
protected int count;
public ByteArrayOutputStream() {
this(32);
}
public ByteArrayOutputStream(int size) {
if (size 〈 0) {
throw new IllegalArgumentException("Negative initial size: " + size);
}
buf = new byte[size];
}
public synchronized void write(int b) {
int newcount = count + 1;
if (newcount 〉 buf.length) {
byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
buf[count] = (byte)b;
count = newcount;
}
...
}