設計模式學習(九):裝飾器模式

2022-11-10 21:00:18

設計模式學習(九):裝飾器模式

作者:Grey

原文地址:

部落格園:設計模式學習(九):裝飾器模式

CSDN:設計模式學習(九):裝飾器模式

裝飾器模式

裝飾器模式是一種結構型模式。

顧名思義,就是對某個方法或者物件進行裝飾,舉個簡單的例子,有個圓形類 Circle,我需要把這個圓形的塗上紅色,其實就是新增一個裝飾器來裝飾這個圓形類。

如果要讓裝飾器通用一些,可以處理圓形類對應的抽象類 Sharp ,那麼對於任意 Sharp 的子類,都可以用紅色裝飾器來塗紅色。

範例程式碼如下

我們先定義 Sharp 這個抽象類

public abstract class Sharp {
    protected abstract void draw();
}

然後我們定義 Sharp 的裝飾類 SharpDecorator ,這個類是所有裝飾器類的抽象類,後續的裝飾器只需要實現這個抽象類就可以對 Sharp 進行各種裝飾了,

public abstract class SharpDecorator extends Sharp {
    protected Sharp decoratedSharp;

    public SharpDecorator(Sharp decoratedSharp) {
        this.decoratedSharp = decoratedSharp;
    }
}

紅色裝飾器實現這個抽象類即可:

public class RedSharpDecorator extends SharpDecorator {
    public RedSharpDecorator(Sharp decoratedSharp) {
        super(decoratedSharp);
    }

    private static void redIt() {
        System.out.println("[RED]");
    }

    @Override
    protected void draw() {
        redIt();
        this.decoratedSharp.draw();
        redIt();
    }
}

主方法呼叫的時候只需要:

new RedSharpDecorator(new Circle()).draw();

UML 圖如下:

說明:

  1. 裝飾器類和原始類繼承同樣的父類別,這樣我們可以對原始類「巢狀」多個裝飾器類。

  2. 裝飾器類是對功能的增強,這也是裝飾器模式應用場景的一個重要特點。符合「組合關係」這種程式碼結構的設計模式有很多,比如代理模式橋接模式,還有現在的裝飾器模式。儘管它們的程式碼結構很相似,但是每種設計模式的意圖是不同的。就拿比較相似的代理模式和裝飾器模式來說:

代理模式中,代理類附加的是跟原始類無關的功能;

裝飾器模式中,裝飾器類附加的是跟原始類相關的增強功能。

裝飾器模式應用

在 JDK 中,BufferedInputStream 、 DataInputStream 並非繼承自 InputStream ,而是另外一個叫 FilterInputStream 的類。

這是因為 InputStream 是一個抽象類而非介面,而且它的大部分函數(比如 read()available())都有預設實現,按理來說,我們只需要在 BufferedInputStream 類中重新實現那些需要增加快取功能的函數就可以了,其他函數只需要複用 InputStream 的預設實現。但實際上,這樣做是行不通的。對於即便是不需要增加快取功能的函數來說,BufferedInputStream 還是必須把它重新實現一遍,簡單包裹對 InputStream 物件的函數呼叫。那 BufferedInputStream 類就無法將最終讀取資料的任務,委託給傳遞進來的 InputStream 物件來完成,DataInputStream 也存在跟 BufferedInputStream 同樣的問題。為了避免程式碼重複,Java I/O 包中抽象出了一個裝飾器父類別 FilterInputStream,包裝了 InputStream 的預設實現

package java.io;

public class FilterInputStream extends InputStream {

    protected volatile InputStream in;

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

 
    public int read() throws IOException {
        return in.read();
    }

    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);
    }

    public long skip(long n) throws IOException {
        return in.skip(n);
    }

    public int available() throws IOException {
        return in.available();
    }

    public void close() throws IOException {
        in.close();
    }

    public synchronized void mark(int readlimit) {
        in.mark(readlimit);
    }

    public synchronized void reset() throws IOException {
        in.reset();
    }

    public boolean markSupported() {
        return in.markSupported();
    }
}

InputStream 的所有的裝飾器類( BufferedInputStream 和 DataInputStream )都繼承自這個裝飾器父類別。這樣,裝飾器類只需要實現它需要增強的方法就可以了,其他方法繼承裝飾器父類別的預設實現。

其他應用

  • Java 中的 I/O 流, Read/InputStream ,Write/OutputStream

  • Java 中的 UnmodifiableCollection

  • Spring 中的 HttpHeadResponseDecorator, 還有對 Cache 的裝飾類 TransactionAwareCacheDecorator

UML 和 程式碼

UML 圖

程式碼

更多

設計模式學習專欄

參考資料