結構型-裝飾器

小弟季義欽發表於2013-03-28
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); //根據大小計算價格
	}
}

相關文章