本文整理自百度文库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"));
}
}