定義
裝飾者模式:在不改變原類文件以及不使用繼承的情況下,動態地將責任附加到對象上,從而實現動態拓展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
設計原則
開放-關閉原則:類應該對拓展開放,對修改關閉
類圖
1、Component是基類。通常是一個抽象類或者一個接口,定義了屬性或者方法,方法的實現可以由子類實現或者自己實現。通常不會直接使用該類,而是通過繼承該類來實現特定的功能,它約束了整個繼承樹的行爲。比如說,如果Component代表人,即使通過裝飾也不會使人變成別的動物。
2、ConcreteComponent是Component的子類,實現了相應的方法,它充當了“被裝飾者”的角色。
3、Decorator也是Component的子類,它是裝飾者共同實現的抽象類(也可以是接口)。比如說,Decorator代表衣服這一類裝飾者,那麼它的子類應該是T恤、裙子這樣的具體的裝飾者。
4、ConcreteDecorator是Decorator的子類,是具體的裝飾者,由於它同時也是Component的子類,因此它能方便地拓展Component的狀態(比如添加新的方法)。每個裝飾者都應該有一個實例變量用以保存某個Component的引用,這也是利用了組合的特性。在持有Component的引用後,由於其自身也是Component的子類,那麼,相當於ConcreteDecorator包裹了Component,不但有Component的特性,同時自身也可以有別的特性,也就是所謂的裝飾。
實例
下面就以一個經典案例星巴克咖啡說明:
Step 1、創建Component基類
public abstract class Beverage {
protected String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
Step 2、創建被裝飾者——ConcreteComponent
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Dark Roast Coffee";
}
@Override
public double cost() {
return .99;
}
}
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
@Override
public double cost() {
return .89;
}
}
public class LowCaffeine extends Beverage {
public LowCaffeine() {
description = "Low Caffeine Coffee";
}
@Override
public double cost() {
return 1.05;
}
}
Step 3、創建裝飾者基類-Decorator
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
Step 4、創建具體裝飾者-ConcreteDecorator
public class Milk extends CondimentDecorator {
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Milk";
}
@Override
public double cost() {
return beverage.cost() + .10;
}
}
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Mocha";
}
@Override
public double cost() {
return beverage.cost() + .20;
}
}
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Soy";
}
@Override
public double cost() {
return beverage.cost() + .15;
}
}
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Whip";
}
@Override
public double cost() {
return beverage.cost() + .10;
}
}
step5:使用
Beverage beverage = new HouseBlend();
beverage = new Soy(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
Log.e("Test",beverage.getDescription() + " ¥" + beverage.cost());
邏輯圖:
特點
以上就是裝飾者模式的一個小栗子,講述了裝飾者的基本用法。通過上述的例子,我們可以總結一下裝飾者模式的特點。
(1)裝飾者和被裝飾者有相同的接口(或有相同的父類)。
(2)裝飾者保存了一個被裝飾者的引用。
(3)裝飾者接受所有客戶端的請求,並且這些請求最終都會返回給被裝飾者(參見韋恩圖)。
(4)在運行時動態地爲對象添加屬性,不必改變對象的結構。
使用裝飾者模式的最大好處就是其拓展性十分良好,通過使用不同的裝飾類來使得對象具有多種多樣的屬性,靈活性比直接繼承好。然而它也有缺點,那就是會出現很多小類,即裝飾類,使程序變得複雜。
應用
學習了裝飾者模式用法、特點以及優缺點後,我們再來看看裝飾者模式在實際開發過程的應用。裝飾者模式在Java中經常出現的地方就是JavaIO。提到JavaIO,腦海中就冒出了大量的類:InputStream、FileInputStream、BufferedInputStream……等,真是頭都大了,其實,這裏面大部分都是裝飾類,只要弄清楚這一點就容易理解了。我們來看看JavaIO是怎樣使用裝飾者模式的。
從字符流來分析,我們知道,有兩個基類,分別是InputStream和OutputStream,它們也就是我們上面所述的Component基類。接着,它有如下子類:FileInputStream、StringBufferInputStream等,它們就代表了上面所述的ConcreteComponent,即裝飾對象。此外,InputStream還有FilterInputStream這個子類,它就是一個抽象裝飾者,即Decorator,那麼它的子類:BufferedInputStream、DataInputStream等就是具體的裝飾者了。那麼,從裝飾者模式的角度來看JavaIO,是不是更加容易理解了呢?