package decorator;
/**
* @author jiq
* 型別:Structural
* 定義:動態地將責任附加到物件上
* 若要擴充功能,裝飾器提供了比繼承更加有彈性的替代方案。
* OO原則: 類應該 對修改關閉,對擴充開放!!!
*
1 作用:
對擴充開放,對修改關閉。
避免繼承濫用,學會使用組合,在執行時裝飾類。
2 為什麼要這麼做呢?
可以在不修改任何底層程式碼的情況下(<免於改變>),給你的(或者別人的)物件賦予新的職責(<易於擴充>)。
這就是著名的“開放-關閉”原則: 類應當對擴充開放,對修改關閉。
3 實際用法:
可見除了繼承,裝飾者模式也可以讓我們擴充行為。
裝飾者模式意味著一群裝飾者類,這些類用來包裝具體的元件。
你可以用無數裝飾者來包裝(組合)一個元件,而不需要更改任何原有的元件。
4 已有案例:
Java IO流中的各種包裝類就是典型的裝飾者。
* *******************************************************
* 類之間的關係:
* 元件抽象類:Beverage
* 裝飾器抽象基類: CondimentDecorator 也繼承自該抽象元件(為了迭代裝飾)
*
* 實際的Beverage(家用咖啡,特濃咖啡等)繼承自該抽象元件 Beverage
* 各種裝飾器(調料,大小等)繼承自裝飾器抽象基類CondimentDecorator
*
* 用各種裝飾器裝飾實際的Beverage
*
* 如果只是用繼承,每一種飲料都繼承自Beverage,各種調料以及飲料的組合都需要一個類,這簡直就是類爆炸!!!
*/
/**
* our company make beverage (元件基類)
*/
abstract class Beverage{
protected String description = "unKown Beverage";
public String getDescription(){
return description;
}
/** calculate price of this beverage */
public abstract double cost();
}
/**
* Decorator(調料 - 裝飾器基類)
* 問題: 為什麼裝飾者還要從Beverage繼承呢?
* 為了能夠使得具體的裝飾器能夠被其他裝飾器裝飾(擴充)。
* 而裝飾器只能裝飾(擴充)Beverage。
* */
abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
///////////////////////////////////////////////////////////
//特濃咖啡(元件)
class Espresso extends Beverage{
public Espresso(){description = "Espresso";}
public double cost() {return 0.9;}
}
//家常咖啡(元件)
class HouseBlend extends Beverage{
public HouseBlend(){description = "HouseBlend"; }
public double cost() {return 1.0;}
}
///////////////////////////////////////////////////////////////
//用Mocha(裝飾器)來裝飾一杯beverage
class Mocha extends CondimentDecorator{
Beverage beverage; //wrap a beverage(組合)
public Mocha(Beverage beverage){this.beverage = beverage;}
public String getDescription() {
return beverage.getDescription() + ", add Mocha";
}
public double cost() {
return 0.20 + beverage.cost();
}
}
//用豆漿(裝飾器)來裝飾一杯beverage
class Soy extends CondimentDecorator{
Beverage beverage; //wrap a beverage(組合)
public Soy(Beverage beverage){this.beverage = beverage;}
public String getDescription() {
return beverage.getDescription() + ", add Soy";
}
public double cost() {
return 0.30 + beverage.cost();
}
}
public class Starbuzz {
public static void main(String[] args) {
/**
* 製造一杯Espresso咖啡,什麼調料也不放
* */
Beverage b1 = new Espresso();
System.out.println(b1.getDescription() + " $" + b1.cost());
/**
* 製造一杯HoseBlend咖啡,放入豆漿和摩卡
* */
Beverage b2 = new HouseBlend();
Beverage newBeverage = new Mocha(new Soy(b2));
System.out.println(newBeverage.getDescription() + " $" + newBeverage.cost());
///////////////////////////////////////////////////////////////
/**
* 思考:如果生成的飲料決定從今天開始加上容量大小。
* 供顧客挑選大杯,中杯以及小杯,然後根據飲料容量
* 來收費,我們應該怎麼改變裝飾者來應對這樣的需求???
* 很顯然,我們不能更改現有元件,因為我們的原則就是對修改關閉。
* 那怎麼擴充呢??? 答案是增加一種裝飾器。
* 裝飾大小的裝飾器,可以設定大小,更新價格。
* */
/**
* 製造一杯HoseBlend咖啡,放入豆漿,設定為中杯
* */
Beverage b3 = new HouseBlend();
Beverage b4 = new SizeDecorator(new Soy(b3), 1); //設定大小
System.out.println(b4.getDescription() + " $" + b4.cost());
//是不是很爽啊?對修改關閉,對擴充任意開放
}
}
/** 擴充一個設定飲料大小,並且根據大小計算價格的裝飾器 */
class SizeDecorator extends CondimentDecorator{
Beverage beverage; //wrap a beverage
int size; //擴充的屬性
public SizeDecorator(Beverage beverage, int size){
this.beverage = beverage;
this.size = size;
}
//擴充的方法
public void setSize(int size){ this.size = size; }
//擴充的方法
public int getSize(){ return size; }
public String getDescription() {
return beverage.getDescription() + ", set size:" + size;
}
public double cost() {
return beverage.cost() * (0.5 + size/10); //根據大小計算價格
}
}