在現實生活中,常常需要對現有產品增加新的功能或美化其外觀,如房子裝修、相片加相框等。在軟件開發過程中,有時想用一些現存的組件。這些組件可能只是完成了一些核心功能。但在不改變其結構的情況下,可以動態地擴展其功能。所有這些都可以釆用裝飾模式來實現。
裝飾模式的定義與特點
裝飾(Decorator)模式的定義:指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬於對象結構型模式。
- 采用裝飾模式擴展對象的功能比采用繼承方式更加靈活。
- 可以設計出多個不同的具體裝飾類,創造出多個不同行為的組合。
其主要缺點是:裝飾模式增加了許多子類,如果過度使用會使程序變得很復雜。
UML類圖

應用實例
星巴克咖啡訂單項目(咖啡館):
-
咖啡種類/單品咖啡:Espresso(意大利濃咖啡)、ShortBlack、LongBlack(美式 咖啡)、Decaf(無因咖啡)
-
調料:Milk、Soy(豆漿)、Chocolate
-
要求在擴展新的咖啡種類時,具有良好的擴展性、改動方便、維護方便
-
使用OO的來計算不同種類咖啡的費用: 客戶可以點單品咖啡,也可以單品咖 啡+調料組合

代碼示例
首先抽象出一個Drink類,它是一個抽象構件(Component)角色:定義一個抽象接口以規范准備接收附加責任的對象
public abstract class Drink {
public String des; // 描述
private float price = 0.0f;
// 省略setter and getter方法
//計算費用的抽象方法
//子類來實現
public abstract float cost();
接下來是具體構件角色,但在這里,我們有Espresso,ShortBlack、LongBlack等,所以我們定義一個Coffee的緩沖層,因為獲取價格的這個方法,下放到具體子類也是一樣的,所有加了一個緩沖層。
public class Coffee extends Drink {
@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice();
}
}
接下來就是一個具體的構件角色了,
public class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
public class ShortBlack extends Coffee{
public ShortBlack() {
setDes(" shortblack ");
setPrice(4.0f);
}
}
public class DeCaf extends Coffee {
public DeCaf() {
setDes(" 無因咖啡 ");
setPrice(1.0f);
}
}
以上是四個具體的構件角色。
現在要往咖啡中加調料:Milk、Soy(豆漿)、Chocolate 等
我們也是通過抽象裝飾(Decorator)角色:繼承抽象構件,並包含具體構件的實例,可以通過其子類擴展具體構件的功能。
public class Decorator extends Drink {
private Drink obj;
public Decorator(Drink obj) { //組合
// TODO Auto-generated constructor stub
this.obj = obj;
}
@Override
public float cost() {
// TODO Auto-generated method stub
// getPrice 自己價格
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
// obj.getDes() 輸出被裝飾者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
抽象裝飾中聚合了Drink obj,那后續在使用中,我們通過具體傳入Espresso、ShortBlack等來裝配。
具體裝飾
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 豆漿 ");
setPrice(1.5f);
}
}
//具體的Decorator, 這里就是調味品
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 調味品 的價格
}
}
使用測試
public class CoffeeBar {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 裝飾者模式下的訂單:2份巧克力+一份牛奶的LongBlack
// 1. 點一份 LongBlack
Drink order = new LongBlack();
System.out.println("費用1=" + order.cost());
System.out.println("描述=" + order.getDes());
// 2. order 加入一份牛奶
order = new Milk(order);
System.out.println("order 加入一份牛奶 費用 =" + order.cost());
System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
// 3. order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入一份巧克力 費用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
// 3. order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入2份巧克力 費用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
System.out.println("===========================");
Drink order2 = new DeCaf();
System.out.println("order2 無因咖啡 費用 =" + order2.cost());
System.out.println("order2 無因咖啡 描述 = " + order2.getDes());
order2 = new Milk(order2);
System.out.println("order2 無因咖啡 加入一份牛奶 費用 =" + order2.cost());
System.out.println("order2 無因咖啡 加入一份牛奶 描述 = " + order2.getDes());
}
}
測試結果:
費用1=5.0
描述= longblack
order 加入一份牛奶 費用 =7.0
order 加入一份牛奶 描述 = 牛奶 2.0 && longblack
order 加入一份牛奶 加入一份巧克力 費用 =10.0
order 加入一份牛奶 加入一份巧克力 描述 = 巧克力 3.0 && 牛奶 2.0 && longblack
order 加入一份牛奶 加入2份巧克力 費用 =13.0
order 加入一份牛奶 加入2份巧克力 描述 = 巧克力 3.0 && 巧克力 3.0 && 牛奶 2.0 && longblack
===========================
order2 無因咖啡 費用 =1.0
order2 無因咖啡 描述 = 無因咖啡
order2 無因咖啡 加入一份牛奶 費用 =3.0
order2 無因咖啡 加入一份牛奶 描述 = 牛奶 2.0 && 無因咖啡
以上就是裝飾者設計模式的簡單應用了,JDK源碼中的IO流就充分使用了裝飾者設計模式。
IO流中的裝飾者設計模式
以InputStream為例,

InputStream就是抽象構件(Component)角色
FilterInputStream就是抽象裝飾角色(Decorator),繼承並持有一個InputStram的引用
剩下兩個角色自己對應,
