Java設計模式之裝飾者模式(Decorator pattern)

_小馬快跑_發表於2017-12-15

裝飾者模式: 動態的給物件新增一些額外的屬性或行為。相比於使用繼承,裝飾者模式更加靈活。

UML圖:

裝飾者.png

一般來說裝飾者模式有下面幾個參與者:

  • Component:裝飾者和被裝飾者共同的父類,是一個介面或者抽象類,用來定義基本行為
  • ConcreteComponent:定義具體物件,即被裝飾者
  • Decorator:抽象裝飾者,繼承自Component,從外類來擴充套件ConcreteComponent。對於ConcreteComponent來說,不需要知道Decorator的存在,Decorator是一個介面或抽象類
  • ConcreteDecorator:具體裝飾者,用於擴充套件ConcreteComponent

注:裝飾者和被裝飾者物件有相同的超型別,因為裝飾者和被裝飾者必須是一樣的型別,這裡利用繼承是為了達到型別匹配,而不是利用繼承獲得行為。

利用繼承設計子類,只能在編譯時靜態決定,並且所有子類都會繼承相同的行為;利用組合的做法擴充套件物件,就可以在執行時動態的進行擴充套件。裝飾者模式遵循開放-關閉原則:**類應該對擴充套件開放,對修改關閉。**利用裝飾者,我們可以實現新的裝飾者增加新的行為而不用修改現有程式碼,而如果單純依賴繼承,每當需要新行為時,還得修改現有的程式碼。

例項:

假設一家甜品店,出售蛋糕,除了蛋糕外,還可以在蛋糕上佈置水果,蠟燭等,但是水果和蠟燭需要額外收費,**假設一個蛋糕的價格是66元,水果和蠟燭分別需要額外付10元,那麼怎麼樣來動態的計算價格呢?**例子所貼程式碼已上傳Github:裝飾者模式。 首先,定義元件類,也是裝飾者和被裝飾者的超類Sweet .java:

public abstract class Sweet {
    String description = "Sweet";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}
複製程式碼

定義被裝飾者蛋糕類,Cake .java:

public class Cake extends Sweet {
    @Override
    public String getDescription() {
        return "一個蛋糕";
    }

    @Override
    public double cost() {
        return 66;
    }
}
複製程式碼

定義抽象裝飾者類Decorator.java:

public abstract class Decorator extends Sweet {
    public abstract String getDescription();
}
複製程式碼

定義具體裝飾者水果類,FruitDecorator.java:

public class FruitDecorator extends Decorator {
    Sweet sweet;

    public FruitDecorator(Sweet sweet) {
        this.sweet = sweet;
    }

    @Override
    public String getDescription() {
        return sweet.getDescription() + ",水果";
    }

    @Override
    public double cost() {
        return sweet.cost() + 10;
    }
}
複製程式碼

定義具體裝飾者蠟燭類,CandleDecorator.java:

public class CandleDecorator extends Decorator {
    Sweet sweet;

    public CandleDecorator(Sweet sweet) {
        this.sweet = sweet;
    }

    @Override
    public String getDescription() {
        return sweet.getDescription() + ",蠟燭";
    }

    @Override
    public double cost() {
        return sweet.cost() + 10;
    }
}
複製程式碼

最後根據不同的選擇來結算價格:

public static void main(String[] args) {

    Cake cake = new Cake();
    System.out.println(cake.getDescription() + "總共花費" + cake.cost());

    FruitDecorator fruitDecorator = new FruitDecorator(cake);
    System.out.println(fruitDecorator.getDescription() + "總共花費" + fruitDecorator.cost());

    CandleDecorator candleDecorator = new CandleDecorator(fruitDecorator);
    System.out.println(candleDecorator.getDescription() + "總共花費" + candleDecorator.cost());

    }
複製程式碼

完整程式碼地址已上傳Github:裝飾者模式。 執行結果:

一個蛋糕,總共花費66.0
一個蛋糕,水果,總共花費76.0
一個蛋糕,水果,一根蠟燭,總共花費86.0
複製程式碼

可見,裝飾者模式可以非常靈活地動態地給被裝飾者新增新行為,它的缺點也顯現出來了,那就是必須管理好更多的物件,但是,裝飾者模式可以和工廠模式或生成器這樣的模式一塊使用來避免這個問題。

PS:我們使用java.IO類時類似於下面的這種寫法:

new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream( "io.txt" )));
複製程式碼

java.IO類用到的也是裝飾者模式!是不是一下清楚了好多~

總結: 1、裝飾者和被裝飾者物件有相同的超型別,所以在任何需要原始物件(被裝飾者)的場合,都可以用裝飾過得物件代替原始物件。 2、可以用一個或多個裝飾者包裝一個物件(被裝飾者) 3、裝飾者可以在所委託的裝飾者行為之前或之後加上自己的行為,以達到特定的目的 4、被裝飾者可以在任何時候被裝飾,所以可以在執行時動態地、不限量地用你喜歡的裝飾者來裝飾物件。 5、裝飾者會導致出現很多小物件,如果過度使用,會讓程式變得複雜。

相關文章