本文整理自百度文庫ppt:http://wenku.baidu.com/link?url=vWUj0YhlLr0C2X8s9tZ_UkZN9pUmgDQz2G7kCAiPLkP83HLaQBQtnSGrJQJ4typ-Av0tqetq-a6T6b7uPTq2_K17j0vGXcypoFCU32YT-dC
案例:星巴茲(Starbuzz)咖啡訂購系統。
最初設計的訂購系統如下圖:
購買咖啡時,每一種咖啡中能添加一種或幾種調料:steamed milk(蒸煮的牛奶), soy(醬油), mocha(摩卡,也稱爲巧克力),和 whipped milk (加了甜點心的牛奶)。每一種調料都要收一點錢,星巴茲(Starbuzz)咖啡訂購系統變成下面的樣子:
顯然上面這種設計是不合理的,後面設計人員再次做了改動,修改後如下圖
這個設計的缺陷
如果調料的價格改變,我們需要修改現存的代碼。
如果增加了新的調料,我們需要在基類增加新的方法以及修改cost()方法。
我們也可能有新的飲料類型。一些飲料類型,例如,冰茶(ice tea),現有的調料可能是不合適的。但是,茶子類仍然後繼承基類的調料方法。
如果客戶需要雙份mocha,怎麼辦呢?
我們已經看到:附加調料的飲料定價模式,用繼承表示是不合適的;在基類增加調料的實例變量和方法,對一些子類也是不合適的。 這裏,我們試試:以飲料爲主體,用調料“裝飾”飲料。例如,如果客戶需要Dark Roast(焦炒咖啡) ,添加Mocha(摩卡)和Whip(甜點心)。
我們可以:
取DarkRoast (焦炒咖啡)對象;
用Mocha (摩卡)對象裝飾它;
用Whip (甜點心)對象裝飾它;
調用cost()方法計算價格,總價格的計算需要委託。
但是,我們怎樣裝飾一個對象?又怎樣委託?
裝飾者模式,動態地給對象添加職責,就擴展功能而言,裝飾者比派生子類提供了更好的柔性。
接下來我們看星巴克訂購的實現代碼:
/**
* @author pengl
* 2014-5-7
* 描述:被裝飾基類 (組件接口) 飲品
*/
public abstract class Beverage {
protected String description;
/**
* 飲品描述信息
* @return
*/
protected String getDescription(){
return description;
}
/**
* 計算價格
* @return
*/
protected abstract double cost();
}
/**
* @author pengl
* 2014-5-7
* 描述:被裝飾者子類(具體組件) 焦烤咖啡
*/
public class DarkRoast extends Beverage{
public DarkRoast(){
description = "焦烤咖啡";
}
@Override
protected double cost() {
return 2.5;
}
}
/**
* @author pengl
* 2014-5-7
* 描述:被裝飾者子類(具體組件) 家庭混合咖啡
*/
public class HouseBlend extends Beverage{
public HouseBlend(){
description = "家庭混合咖啡";
}
@Override
protected double cost() {
return 1.8;
}
}
/**
* @author pengl
* 2014-5-8
* 描述:裝飾者基類
*/
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
/**
* @author pengl
* 2014-5-8
* 描述:裝飾者實現類 摩卡
*/
public class Mocha extends CondimentDecorator{
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Mocha";
}
@Override
protected double cost() {
return 0.7 + beverage.cost();
}
}
/**
* @author pengl
* 2014-5-8
* 描述:裝飾者實現類 牛奶
*/
public class Milk extends CondimentDecorator{
Beverage beverage;
public Milk(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Milk";
}
@Override
protected double cost() {
return 0.2 + beverage.cost();
}
}
/**
* @author pengl
* 2014-5-8
* 描述:客戶端測試類
*/
public class Main {
public static void main(String[] args) {
Beverage beverage = new DarkRoast(); // 焦烤咖啡
System.out.println(beverage.getDescription() + " $" + beverage.cost());
beverage = new Mocha(beverage); //添加摩卡
System.out.println(beverage.getDescription() + " $" + beverage.cost());
beverage = new Milk(beverage); //添加牛奶
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new Milk(new HouseBlend()); // 家庭混合咖啡加摩卡加牛奶
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
接下來我以我公司的業務寫的一個測試裝飾者模式代碼
/**
* @author pengl
* 2014-5-9
* 描述:客戶端測試類
*/
public class Main {
public static void main(String[] args) {
IGetLoginInfo loginInfo = new GetLoginBasicInfo();
//只要基本信息
System.out.println(loginInfo.getLoginInfo().get("custName")+"--"+loginInfo.getLoginInfo().get("custId"));
//要基本信息和套餐速率信息
loginInfo = new GetLoginPackageAndSpeed(new GetLoginBasicInfo());
System.out.println(loginInfo.getLoginInfo().get("custName")+"--"+loginInfo.getLoginInfo().get("custId")
+"--"+loginInfo.getLoginInfo().get("speed")+"--"+loginInfo.getLoginInfo().get("package"));
//要基本信息,套餐速率信息 和費用信息
loginInfo = new GetLoginAccountBalance(new GetLoginPackageAndSpeed(new GetLoginBasicInfo()));
System.out.println(loginInfo.getLoginInfo().get("custName")+"--"+loginInfo.getLoginInfo().get("custId")
+"--"+loginInfo.getLoginInfo().get("speed")+"--"+loginInfo.getLoginInfo().get("package")
+"--"+loginInfo.getLoginInfo().get("balance")+"--"+loginInfo.getLoginInfo().get("lastDate"));
}
}