圖解Java設計模式之裝飾者模式 星巴克咖啡訂單項目(咖啡館) 方案 1 - 解決星巴克咖啡訂單項目 方案1 - 解決星巴克咖啡訂單問題分析 方案 2 - 解決星巴克咖啡訂單(好點) 方案2 - 解決星巴克咖啡訂單問題分析 裝飾者模式定義 裝飾者模式原理 裝飾者模式解決星巴克咖啡訂單 裝飾者模式下的 ...
圖解Java設計模式之裝飾者模式
星巴克咖啡訂單項目(咖啡館)
1)咖啡種類/單品咖啡 :Espresso(義大利濃咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(無因咖啡)
2)調料 :Milk、Soy(豆漿)、Chocolate
3)要求在擴展新的咖啡種類時,具有良好的擴展性、改動方便、維護方便
4)使用OO的來計算不同種類咖啡的費用 :客戶可以點單品咖啡,也可以單品咖啡 + 調料組合。
方案 1 - 解決星巴克咖啡訂單項目
方案1 - 解決星巴克咖啡訂單問題分析
1)Drink是一個抽象類,表示飲料
2)des就是對咖啡的描述,比如咖啡的名字
3)cost()方法就是計算費用,Drink類中做成一個抽象方法
4)Decaf就是單品咖啡,繼承Drink,並實現cost
5)Espress&&Milk就是單品咖啡 + 調料,這個組合很多
6)問題 :這樣設計,會有很多類,當我們增加一個單品咖啡,或者一個新的調料,類的數量就會倍增,就會出現類爆炸。
方案 2 - 解決星巴克咖啡訂單(好點)
1)前面分析到方案1因為咖啡單品 + 調料組合會造成類的倍增,因此可以做改進,將調料內置到Drink類,這樣就不會造成類數量過多。從而提高項目的維護性。
2)說明 :milk,soy,chocolate可以設計為Boolean,表示是否要添加相應的調料。
方案2 - 解決星巴克咖啡訂單問題分析
1)方案2可以控制類的數量,不至於造成很多的類
2)在增加或者刪除調料種類時,代碼的維護量很大
3)考慮到用戶可以添加多份調料時,可以將hasMilk返回一個對於int
4)考慮使用裝飾者模式
裝飾者模式定義
1)裝飾者模式 :動態的將新功能附加到對象上。在對象功能擴展方面,它比繼承更有彈性,裝飾者模式也體現類開閉原則(ocp)。
裝飾者模式原理
1)裝飾者模式就像打包一個快遞
主體 :比如 :陶瓷、衣服(Component)//被裝飾者
包裝 :比如 :報紙填充、塑料泡沫、紙板、木板(Decorator裝飾者)
2)Component主體 :比如類似前面的Drink
3)ConcreteComponent和Decorator
ConcreteComponent :具體的主體,
比如前面的各個單品咖啡
4)Decorator :裝飾者,比如各調料。
如圖的Component和ConcreteComponent之間,如果ConcreteComponent類很多,還可以設計一個緩存層,將共有的部分提取出來,抽象成一個類。
裝飾者模式解決星巴克咖啡訂單
說明 :
1)Drink類就是前面說的抽象類,Component
2)ShortBlack就單品咖啡
3)Decorator是一個裝飾類,含有一個被裝飾的對象(Drink drink)
4)Decorator的cost方法進行一個費用的疊加計算,遞歸的計算價格
裝飾者模式下的訂單 :2份巧克力 + 一份牛奶的LongBlack
說明 :
1)Milk包含類LongBlack
2)一份Chocolate包含了(Milk + LongBlack)
3)一份Chocolate包含了(Chocolate + Milk + LongBlack)
4)這樣不管是什麼形式的單品咖啡 + 調料組合,通過遞歸方式可以方便的組合和維護。
裝飾者模式咖啡訂單項目應用實例
package com.example.demo.decorator;
public abstract class Drink {
/**
* 描述
*/
public String des;
private float price = 0.0f;
/**
* 計算費用的抽象方法
* 子類去實現
* @return
*/
public abstract float cost();
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;
}
}
package com.example.demo.decorator;
public class Decorator extends Drink{
private Drink obj;
/**
* 組合的方式
* @param objDrink
*/
public Decorator(Drink objDrink) {
this.obj = objDrink;
}
@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();
}
}
package com.example.demo.decorator;
/**
* 具體的Decotator,這裡就是調味品
* @author zhaozhaohai
*
*/
public class Chocolate extends Decorator{
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
// 調位品的價格
setPrice(3.0f);
}
}
package com.example.demo.decorator;
public class Coffee extends Drink{
@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice();
}
}
package com.example.demo.decorator;
public class DeCof extends Coffee{
public DeCof() {
setDes(" 無因咖啡 ");
setPrice(9.6f);
}
}
package com.example.demo.decorator;
public class CoffeeBar {
public static void main(String[] args) {
// 裝飾者模式下的訂單 :2份巧克力 + 一份牛奶的LongBlack
// 1. 點一份 LongBlack
Drink orderDrink = new LongBlack();
System.out.println("費用 1 = " + orderDrink.cost());
System.out.println("描述 = " + orderDrink.getDes());
// 2. orderDrink 加一份牛奶
orderDrink = new Milk(orderDrink);
System.out.println("order 加一份牛奶 費用 = " + orderDrink.cost());
System.out.println("order 加一份牛奶 描述 = " + orderDrink.getDes());
// 3. orderDrink 加一份巧克力
orderDrink = new Chocolate(orderDrink);
System.out.println("order 加一份牛奶 加一份巧克力 費用 = " + orderDrink.cost());
System.out.println("order 加一份牛奶 加一份巧克力 描述 = " + orderDrink.getDes());
// 4. orderDrink 第二份巧克力
orderDrink = new Chocolate(orderDrink);
System.out.println("order 加一份牛奶 加2份巧克力 費用 = " + orderDrink.cost());
System.out.println("order 加一份牛奶 加2份巧克力 描述 = " + orderDrink.getDes());
System.out.println("=========================");
Drink drink2 = new DeCof();
System.out.println("drink2 無因咖啡 費用 = " + drink2.cost());
System.out.println("drink2 無因咖啡 描述 = " + drink2.getDes());
drink2 = new Milk(drink2);
System.out.println("order2 無因咖啡 加入一份牛奶 費用 = " + drink2.cost());
System.out.println("order2 無因咖啡 加入一份牛奶 描述 = " + drink2.getDes());
}
}
package com.example.demo.decorator;
public class Espresso extends Coffee{
public Espresso() {
setDes(" 義大利咖啡 ");
setPrice(6.6f);
}
}
package com.example.demo.decorator;
public class LongBlack extends Coffee{
public LongBlack() {
setDes(" longBlack ");
setPrice(5.0f);
}
}
package com.example.demo.decorator;
public class Milk extends Decorator{
public Milk(Drink obj) {
super(obj);
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
package com.example.demo.decorator;
public class ShortBlack extends Coffee{
public ShortBlack() {
setDes(" shortBlack ");
setPrice(8.0f);
}
}
package com.example.demo.decorator;
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
setDes(" 豆漿 ");
setPrice(1.5f);
}
}
裝飾者模式在JDK應用的源碼分析
Java的IO結構,FilterInputStream就是一個裝飾者
說明 :
(1)InputStream是抽象類,類似前面的Drink
(2)FileInputStream是InputStream子類,類似前面DeCaf,LongBlack
(3)FilterInputStream是InputStream子類 :類似前面的Decorator裝飾者
(4)DataInputStream是FilterInputStream 子類,具體的裝飾者,類似前面的Milk,SOy等
(5)FIlterInputStream類有protected volatile InputStream in;即含被裝飾者
jdk的io體系中,就是使用的裝飾者模式