装饰者模式
星巴克咖啡是以扩张速度最快而闻名的咖啡连锁店,他们的订单系统起初是这样设计的
子类实现cost方法后即可返回相应咖啡的价钱
但是很快,星巴克又引入了许多调料,例如蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)或奶泡等,要根据加入调料的不同收取不同的费用,所以系统中必须考虑到调料部分
我们拿首选综合咖啡举例,会出现类爆炸的问题
很明显,这种设计方法是非常难以维护的,不仅仅在于类爆炸的问题,当牛奶价格上涨时,需要更改极其多的类的返回价格,当新增一种调料时,又会产生极其多的类,这是维护上的噩梦,那么我们应该怎么解决这个问题呢,我们想到了继承
从基类Beverage下手,加上各种调料的布尔值,在基类中算出我们要加入哪些调料的总价格,加上子类咖啡的价格即可
这样便解决了类爆炸的问题,同时也较利于维护
有一天,星巴克上架了矿泉水,顾客想要一瓶水,同时加一份奶,再加一份豆浆,同时再加双倍摩卡
众所周知,水里面怎么能加奶和豆浆呢,这就产生了功能不适用的问题
双倍摩卡系统也不会给予支持
在设计模式中,类应该是对扩展开放,对修改关闭的,每当我们要上架一种新调料时,上述设计方法都会使我们去更改基类中的代码
那么怎么设计才能使系统没有类爆炸、设计死板的问题,又能解决功能不适用的麻烦,满足设计原则呢?
装饰者模式带给了我们答案
我们要以饮料为主体,然后在运行时以调料来“装饰”饮料
比方说,如果顾客想要摩卡和奶泡深烘培的咖啡,那么我们要做的是:
- 拿一个深烘培咖啡对象
- 以摩卡对象装饰它
- 以奶泡对象装饰它
- 调用cost方法,并将调料的价钱加上去
让我们看看系统是什么样的
调料的具体实现举例:
public class Milk extends CondimentDecorator{
Beverage beverage;
public Milk(Beverage beverage){
this.beverage = beverage;
}
public String getDescription(){
return beverage.getDescription() + ", Milk";
}
public double cost(){
return beverage.cost() + 20;
}
}
订单过程:
Beverage beverage = new HouseBlend(); //创建一个首选综合咖啡
beverage = new Mocha(beverage); //引用指向一个摩卡对象,摩卡对象中包含首选综合咖啡
beverage = new Mocha(beverage); //引用指向一个摩卡对象,摩卡对象中包含一个内摩卡对象,包含的内摩卡对象中包含首选综合咖啡
beverage = new Milk(beverage); //引用指向一个牛奶对象,牛奶对象中包含上述外摩卡对象
System.out.println(beverage.getDescription() + " " + beverage.cost());