1.這個模式可以稱為“給愛用繼承的人一個全新的設計眼界”的模式。牽扯到第五個設計原則:“類應該對擴充開放,而對修改封閉”。但是要注意,遵循這一标準會帶來更多層次上的抽象,增加代碼的複雜度,是以并不是所有類都要這樣設計。
2.文中舉了一個為辛巴克咖啡館寫一個計算咖啡價格+調料價格的類,使用了裝飾模式——動态的将責任附加到對象上,若要擴充功能,裝飾者提供了比繼承更加有彈性的替代方案。我們就拿這個計算咖啡價格的東西舉例子。
3.在原來的設計中,都是繼承于Beverage這個超類中,多一項咖啡+調料組合就多一個子類,最後造成類的爆炸。而使用裝飾模式,我們希望用裝飾器(這裡的調料)一層層的包含被裝飾的咖啡,最後達到通過調用最外層的裝飾者的cost()方法就可以委托其内部計算計算價錢。我們針對這個目标,從代表飲品的Beverage類下手,這是一個基類,代表一個邏輯上的抽象(但不一定要是抽象類,看是否有抽象方法來定),被裝飾者和裝飾者都使用該基類,在這個基類中定義了裝飾者和被裝飾者需要的方法,其中抽象方法是裝飾者和被裝飾者同時都需要的方法,而普通實作的方法則是裝飾者覆寫使用的方法:
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
我們将調料視為裝飾器,這個裝飾器超類為了能夠将要被裝飾的部分包起來,是以要繼承自飲品這個超類:
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
現在我們就構造一些飲品(被裝飾者)
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";//這個變量是繼承而來
public double cost() {
return 1.99;
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
return .89;
然後我們建構具體的裝飾者,比如代表摩卡的Mocha類:
public class Mocha extends CondimentDecorator {
Beverage beverage;//用一個執行個體變量記錄飲品,然後通過構造函數将飲品記錄在執行個體變量中完成裝飾的過程。
public Mocha(Beverage beverage) {
this.beverage = beverage;
public String getDescription() {//這兩個方法都調用了被裝飾部分的相關方法,
return beverage.getDescription() + ", Mocha";
return .20 + beverage.cost();
現在我們測試一下這些代碼,喝杯比較多樣化的咖啡:
public class StarbuzzCoffee {
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()
+ " $" + beverage.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);//一直針對抽象組建類型程式設計
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());
4.總結一下裝飾模式特點:
裝飾者和被裝飾對象有相同的基類--都是來自Beverage這個類。 繼承關系:基類->被裝飾者(就是具體的一些基類延伸),基類->裝飾器->裝飾者(傳入基類)-對應上面的代碼。 你可以用一個或多個裝飾者包裝一個對象--看看beverage3這個對象就知道了。 在任何需要被包裝者的場合可以用裝飾過的對象代替它--比如首先我們在咖啡上加豆漿,然後我們在加豆漿的咖啡上想再加摩卡的話,我們可以直接在這個加過豆漿的咖啡對象上加摩卡。 裝飾者可以在所委托的被裝飾者的行為上加上自己的行為,達到特定目的--getDescription和cost方法充分證明了這一點。
5.JDK中的裝飾模式
最典型的就是IO系統了,比如BufferedInputStream及LineNumberInputStream都擴充自FilterInputStream——這個類是一個抽象的裝飾類。而最高的抽象元件是InputStream類。
我們也可以以假亂真寫一個輸入流類
public class LowerCaseInputStream extends FilterInputStream {
public LowerCaseInputStream(InputStream in) {
super(in);
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char)c));
public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len);
for (int i = offset; i < offset+result; i++) {
b[i] = (byte)Character.toLowerCase((char)b[i]);
}
return result;
public class InputTest {
public static void main(String[] args) throws IOException {
int c;
try {
InputStream in =
new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")));
while((c = in.read()) >= 0) {
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
6.裝飾模式的一些缺陷:
産生各種小類,維護不便。有些代碼會依賴特定的類型,而這樣的代碼一導入裝飾者就出問題了。
總結:
裝飾者模式動态地将責任附加到對象上。若要擴充功能,裝飾者提供了比繼承更有彈性的替代方案
FAQ:
為什麼在一定要有Decorator這個類?
基于上邊的這個例子我們可以看到Decorator這個類隔離了Component中可能出現的具體實作(比如上例中的getDescription方法),之是以要隔離室因為裝飾者對這個方法的實作邏輯和高層的Component類實作的邏輯是不同的。在Decorator将一個方法退化為一個抽象方法,有助于督促具體的裝飾器必須實作這個方法,而不會無意使用Component繼承而來的方法(如果沒有Decorator而直接繼承自Component可能會因為實作邏輯不同而出現沒有實作新邏輯而誤用舊邏輯的情況,這會導緻出現運作中錯誤,而退化為抽象方法後,具體裝飾器則不得不實作這個方法,否則不可能編譯通過,這就将錯誤壓制在了編譯階段,顯然,這相對于運作中出錯是要好得多)。
