Java學習78:Filter模式

在這裏插入圖片描述
Java的IO標準庫提供的InputStream根據來源可以包括:

  • FileInputStream:從文件讀取數據,是最終數據源
  • ServletInputStream:從HTTP請求讀取數據,是最終數據源
  • Socket.getInputStream():從TCP連接讀取數據,是最終數據源

如果我們要給FileInputStream添加緩衝功能,則可以從FileInputStream派生一個類:

BufferedFileInputStream extends FileInputStream

如果要給FileInputStream添加計算簽名的功能,類似的,也可以從FileInputStream派生一個類:

DigestFileInputStream extends FileInputStream

如果要給FileInputStream添加加密/解密功能,還是可以從FileInputStream派生一個類:

CipherFileInputStream extends FileInputStream

如果要給FileInputStream添加緩衝和簽名的功能,那麼我們還需要派生BufferedDigestFileInputStream。如果要給FileInputStream添加緩衝和加解密的功能,則需要派生BufferedCipherFileInputStream。

我們發現,給FileInputStream添加3種功能,至少需要3個子類。這3種功能的組合,又需要更多的子類:
在這裏插入圖片描述
這還只是針對FileInputStream設計,如果針對另一種InputStream設計,很快會出現子類爆炸的情況。

因此,直接使用繼承,爲各種InputStream附加更多的功能,根本無法控制代碼的複雜度,很快就會失控。

爲了解決依賴繼承會導致子類數量失控的問題,JDK首先將InputStream分爲兩大類:

一類是直接提供數據的基礎InputStream,例如:

  • FileInputStream
  • ByteArrayInputStream
  • ServletInputStream

一類是提供額外附加功能的InputStream,例如:

  • BufferedInputStream
  • DigestInputStream
  • CipherInputStream

當我們需要給一個“基礎”InputStream附加各種功能時,我們先確定這個能提供數據源的InputStream,因爲我們需要的數據總得來自某個地方,例如,FileInputStream,數據來源自文件:

InputStream file = new FileInputStream("test.gz");

緊接着,我們希望FileInputStream能提供緩衝的功能來提高讀取的效率,因此我們用BufferedInputStream包裝這個InputStream,得到的包裝類型是BufferedInputStream,但它仍然被視爲一個InputStream:

InputStream buffered = new BufferedInputStream(file);

最後,假設該文件已經用gzip壓縮了,我們希望直接讀取解壓縮的內容,就可以再包裝一個GZIPInputStream:

InputStream gzip = new GZIPInputStream(buffered);

無論我們包裝多少次,得到的對象始終是InputStream,我們直接用InputStream來引用它,就可以正常讀取:
在這裏插入圖片描述
上述這種通過一個“基礎”組件再疊加各種“附加”功能組件的模式,稱之爲Filter模式(或者裝飾器模式:Decorator)。它可以讓我們通過少量的類來實現各種功能的組合:
在這裏插入圖片描述
類似的,OutputStream也是以這種模式來提供各種功能:
在這裏插入圖片描述
編寫FilterInputStream
我們也可以自己編寫FilterInputStream,以便可以把自己的FilterInputStream“疊加”到任何一個InputStream中。

下面的例子演示瞭如何編寫一個CountInputStream,它的作用是對輸入的字節進行計數:
注意到在疊加多個FilterInputStream,我們只需要持有最外層的InputStream,並且,當最外層的InputStream關閉時(在try(resource)塊的結束處自動關閉),內層的InputStream的close()方法也會被自動調用,並最終調用到最核心的“基礎”InputStream,因此不存在資源泄露。


import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Demo04 {
    public static void main(String[] args) throws IOException {
        byte[] data = "hello, world!".getBytes("UTF-8");
        try (CountInputStream input = new CountInputStream(new ByteArrayInputStream(data))) {
            int n;
            while ((n = input.read()) != -1) {
                System.out.println((char)n);
            }
            System.out.println("Total read " + input.getBytesRead() + " bytes");
        }
    }
}
class CountInputStream extends FilterInputStream{

    private int count=0;
    /**
     * Creates a <code>FilterInputStream</code>
     * by assigning the  argument <code>in</code>
     * to the field <code>this.in</code> so as
     * to remember it for later use.
     *
     * @param in the underlying input stream, or <code>null</code> if
     *           this instance is to be created without an underlying stream.
     */
    protected CountInputStream(InputStream in) {
        super(in);
    }
    public int getBytesRead(){
        return this.count;
    }
    public int read() throws IOException{
        int n=in.read();
        if (n!=-1){
            this.count++;
        }
        return n;
    }
    public int read(byte[] b,int off,int len) throws IOException{
        int n=in.read(b,off,len);
        this.count+=n;
        return n;
    }
}

小結
Java的IO標準庫使用Filter模式爲InputStream和OutputStream增加功能:

可以把一個InputStream和任意個FilterInputStream組合;

可以把一個OutputStream和任意個FilterOutputStream組合。

Filter模式可以在運行期動態增加功能(又稱Decorator模式)。

謝謝觀看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章