設計模式第四天之裝飾者模式

   在這次分享之前,我們先來分享一個設計原則------開放-關閉原則。其實在第一篇文章分享策略模式的時候已經使用過了。大家還記得那個android的圖片緩存嗎?大家發現,如果我要新增加一種緩存機制,只需要增加一個類(這個類實現ImageCache接口即可),然後然後通過setImageCache()方法將該類對象賦值給ImageLoader的ImageCache對象,就可以使用了。大家有沒有發現?我在新增這個緩存機制的過程中只是新增了一個類,並沒有對原有的代碼進行修改,這就是遵循了開放-關閉原則。

   開放-關閉原則,我們的目標是允許類容易擴展,在不修改現有代碼的情況下,就可搭配新的行爲。如能實現這樣的目標,有什麼好處呢?這樣的設計具有彈性,可以應對改變,可以接受新的功能類應對改變的需求。(引用《Head First設計模式》)。

   我這次分享的裝飾着模式是完全遵循開放-關閉原則的。

用《Head First設計模式》中的例子來描述吧。

需求是這樣的?我們要爲一個咖啡店設計一個訂單系統。該咖啡店有幾種(HouseBlend、DarkRoast、Decaf,Espresso)原汁原味的咖啡,都是原味的。然後顧客購買咖啡的時候,可以指定添加調料,調料有蒸奶(Steamed Milk)、豆漿(Soy)、摩卡(Mocha)和覆蓋奶泡。

添加調料可以按照客戶的要求添加多種,當然也可以不添加。然後計算出價錢(四種原味的咖啡要錢,調料也要錢)

 大家可以想想你們的餓實現。下面我來分享一個裝飾者模式的實現。

如果顧客想要摩卡和奶泡深焙咖啡。

1.      拿一個深焙咖啡(DarkRoast)對象

2.      以摩卡(Mocha)對象裝飾它

3.      以奶泡(Whip)對象裝飾它

4.      調用cost()方法,並依賴委託delegate將調料的價錢加上去。

《Head First設計模式》中的圖。

首先畫出UML建模圖。


根據uml我們知道所有的主體或者裝飾者都要間接或直接實現相同的接口,這也是裝飾者模式所要求的。

首先來實現飲料的接口Bevarage,有最基本的獲取描述和計算價錢的方法


/**
 * 飲料和調料的公共的接口
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:31
 */
public interface Beverage {

	public double cost();

	public String getDescription();

}

基於飲料的接口,我們實現四種飲料


/**
 * DarkRoast原味飲料
 * 該設計模式中爲主體
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:38
 */
public class DarkRoast implements Beverage {

	private String des;
	private double price;

	public DarkRoast(){
		des="Dark Roase";
		price=1.1;
	}

	@Override
	public double cost(){
		return price;
	}
	@Override
	public String getDescription(){
		return des;
	}
}

/**
 *  * Decaf原味飲料
 * 該設計模式中爲主體
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:39
 */
public class Decaf implements Beverage {

	private String des;
	private double price;

	public Decaf(){
		des="Decaf";
		price=1.15;
	}

	@Override
	public double cost(){
		return price;
	}

	@Override
	public String getDescription(){
		return des;
	}
}

/**
 *  * Espresso原味飲料
 * 該設計模式中爲主體
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:41
 */
public class Espresso implements Beverage {

	private String des;
	private double price;

	public Espresso(){
		des="Espresso";
		price=1.2;
	}
	@Override
	public double cost(){
		return price;
	}

	@Override
	public String getDescription(){
		return des;
	}
}

/**
 *HouseBlend原味飲料
 * 該設計模式中爲主體
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:36
 */
public class HouseBlend implements Beverage {

	private String des;
	private double price;

	public HouseBlend(){
		des="HouseBlend";
		price=1.3;
	}

	@Override
	public double cost(){
		return price;
	}

	@Override
	public String getDescription(){
		return des;
	}
}

}

通過前面的兩個圖,我們知道調料作爲裝飾者,在類中要有一個Beverage飲料接口的一個對象,用來指向前面最新飲料的對象,這樣在算錢的時候,可以先計算出前面的,然後在加上現在增加的。獲取描述也是一樣,先獲取前面的描述再加上現在的描述。那首先我們來實現調料的抽象類。我們知道每個調料類都有Beverage接口指向前面的飲料,那我們可以直接在抽象類中寫這個Beverage對象,然後在各自的調料類中賦值便可以了。(當然price價錢和des描述也是可以這樣做的。)

調料類接口:

/**
 * 調料的接口
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:42
 */
public abstract class  CondimentDecorator implements Beverage {

	protected Beverage beverage;
	@Override
	public abstract double cost();
	
	@Override
	public abstract String getDescription();

}

下面是各種調料。

/**
 * 調料
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:43
 */
public class Milk extends CondimentDecorator {

	private String des;
	private double price;

	/**
	 * 
	 * @param beverage
	 */
	public Milk(Beverage beverage){
		this.beverage = beverage;
		des="Milk";
		price = 0.5;
	}
	@Override
	public double cost(){
		return beverage.cost()+price;//前面的飲料的價錢,再加上當前增加的
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

/**
 * 調料
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:44
 */
public class Mocha extends CondimentDecorator {

	private String des;
	private double price;
	/**
	 * 
	 * @param beverage
	 */
	public Mocha(Beverage beverage){
		this.beverage = beverage;
		des="Mocha";
		price=0.6;
	}

	@Override
	public double cost(){
		return beverage.cost()+price;
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

 * @created 06-11月-2016 10:41:45
 */
public class Soy extends CondimentDecorator {

	private String des;
	private double price;
	/**
	 * 
	 * @param beverage
	 */
	public Soy(Beverage beverage){
		this.beverage = beverage;
		des="Soy";
		price=0.4;
	}

	@Override
	public double cost(){
		return beverage.cost()+price;
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

/**
 * 調料
 * @author wangpeiyu
 * @version 1.0
 * @created 06-11月-2016 10:41:47
 */
public class Whip extends CondimentDecorator {

	private String des;
	private double price;
	/**
	 * 
	 * @param beverage
	 */
	public Whip(Beverage beverage){
		this.beverage = beverage;
		des="Whip";
		price=0.45;
	}

	@Override
	public double cost(){
		return beverage.cost()+price;
	}
	@Override
	public String getDescription(){
		return beverage.getDescription()+" "+des;
	}
}

下面來寫一個測試的例子。

public class main {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//新建一種飲料
		Decaf beverage = new Decaf();
		//原味的錢
		System.out.println(beverage.getDescription()+" $"+beverage.cost());
		//添加了牛奶調料,將主體傳遞進去
		Beverage beverage2 = new Milk(beverage);
		//添加Mocha調料,這時候將前面的最新的飲料傳遞進去
		beverage2 = new Mocha(beverage2);
		//添加Whip飲料,又將最新的飲料傳遞進去
		beverage2 = new Whip(beverage2);
		//查看當前飲料的價錢
		System.out.println(beverage2.getDescription()+" $"+beverage2.cost());
	}
}

結果是:



這樣就實現了客戶想加什麼調料,直接新建一個給調料的類,並將之前的飲料傳給調料裏面的beverage對象即可。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章