裝飾者模式介紹
裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬于結構型模式,它是作為現有的類的一個包裝。這種模式建立了一個裝飾類,用來包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能。
裝飾者模式是動态的将新功能附加到對象上。在對象功能擴充方面,它比繼承更有彈性,裝飾者模式也展現了開閉原則(ocp)。這裡提到的動态的将新功能附加到對象和ocp原則,在後面的應用執行個體上會以代碼的形式展現。
應用場景
星巴克咖啡訂單項目(咖啡館) :
1)咖啡種類/單品咖啡:Espresso(意大利濃咖啡)、ShortBlack、 LongBlack(美式咖啡)、Decaf(無因咖啡)
2)調料:Milk、 Soy(豆 漿)、Chocolate
3)要求在擴充新的咖啡種類時,具有良好的擴充性、改動友善、維護友善
4)使用00來計算不同種類咖啡的費用:客戶可以點單品咖啡,也可以單品咖啡+調料組合。
說明:該場景類似于點奶茶,我點咖啡時,可以選擇單點一份咖啡,也可以選擇向咖啡中加入調料,如牛奶、豆漿等,在結賬時需要計算咖啡的費用+調料的總費用
裝飾者模式原理
就以上面應用場景為例,單品咖啡是被裝飾者,裝飾者是牛奶豆漿等調料,這兩者都繼承一個抽象類(Drink),而在裝飾者(調料)中,封裝了這個抽象類(即将該抽象類寫成了成員變量)。
設計方案
裝飾者模式下的訂單:2份巧克力+1份牛奶的LongBlack
說明:
- Milk包含了LongBlack
- 一份Chocolate 包含了(Milk+LongBlack)
- 一份Chocolate包 含了(Chocolate+Milk+LongBlack)
- 這樣不管是什麼形式的單品咖啡+調料組合,通過遞歸方式可以友善的組合和維護。
具體實作
抽象類:
package com.syc.decorator;
public abstract class Drink {
public String des; // 描述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//計算費用的抽象方法
//子類來實作
public abstract float cost();
}
抽象層:
package com.syc.decorator;
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
美式咖啡:
package com.syc.decorator;
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
意大利咖啡:
package com.syc.decorator;
public class Espresso extends Coffee {
public Espresso() {
setDes("意大利咖啡 ");
setPrice(6.0f);
}
}
裝飾器類:
package com.syc.decorator;
public class Decorator extends Drink {
private Drink obj;
public Decorator(Drink obj) { //組合
this.obj = obj;
}
@Override
public float cost() {
// getPrice 自己價格
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// obj.getDes() 輸出被裝飾者的資訊
return des + " " + getPrice() + " && " + obj.getDes();
}
}
調料牛奶:
package com.atguigu.decorator;
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
調料豆漿:
package com.atguigu.decorator;
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
setDes(" 豆漿 ");
setPrice(1.5f);
}
}
測試類:
package com.syc.decorator;
public class CoffeeBar {
public static void main(String[] args) {
// 裝飾者模式下的訂單: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());
}
}
輸出:
裝飾者模式在JDK應用的源碼分析
在JDK的源碼中,InputStream使用的就是裝飾者模式,其中InputString是抽象類(如Drink類),FilterInputStream是裝飾者(如上面的Decorator),DataInputStream是FilterInputStream的子類(如上面的Milk)
測試類