场景描述:
一个咖啡店,有多种咖啡、多种配料,它们相互组合,配置成一杯咖啡,不同组合价格不同。目前有四种咖啡,分别是HouseBlend(混合咖啡)、DarkRoast(深度烘焙 )、Decaf(无咖啡因)、Espresso(浓咖啡)。配料有milk、soy(豆浆)、Mocha(摩卡)、whip(奶泡)。购买咖啡时选择一种咖啡,多种配置组合。
原本的类图如下,我们要对它进行改造。
问题:
看到上面的类图,如果购买时增加配料要如何计算价格呢,这次本次设计的关键问题。根据开闭原则,我们要对扩展开放,对修改关闭,意思是增加功能时应该是增加新的代码,而不应该修改现在的代码。所以尽量不要更改现在代码,降低引入新bug的风险。可以有这样一个解决方案,在现有的Beverage中加入配料的属性,保存存在的配料集合,这样就能计算价格。但是这个方案违反了开闭原则,使用装饰模式能帮我们避免这个问题。
使用装饰模式计算价格的流程:
需求:顾客需要一杯加了Mocha(摩卡)和whip(奶泡)的DarkRoast咖啡。
计算价格:
其实就是递归调用,从底层开始返回,并相加,得出最后的价格。
装饰模式:
在使用装饰模式实现本次例子前,我们先来了解一下装饰模式。
定义:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
类图:
具体实现:
部分代码没贴上来,代码地址:https://github.com/LiuJinan/designPatternDemo
/**
* Created by LiuJinan on 2017/7/1.
*/
// 属于类图中的Component 组件
public interface Berverage {
public String getDescription();
public double cost();
}
/**
* Created by LiuJinan on 2017/7/1.
*/
//可被装饰的具体组件
public class DarkRoast implements Berverage {
@Override
public String getDescription() {
return "DarkRoast ";
}
@Override
public double cost() {
return 9;
}
}
//可被装饰的具体组件
public class HouseBlend implements Berverage {
@Override
public String getDescription() {
return "HouseBlend ";
}
@Override
public double cost() {
return 10;
}
}
//所有配料装饰者的父类
public interface CondimentDecorator extends Berverage {
}
//装饰者:牛奶
public class Milk implements CondimentDecorator {
private Berverage berverage;
public Milk(Berverage berverage) {
this.berverage = berverage;
}
@Override
public String getDescription() {
return berverage.getDescription() + ", milk";
}
@Override
public double cost() {
return berverage.cost() + 1.5; //milk 1.5元
}
}
//装饰者:Mocha
public class Mocha implements CondimentDecorator {
private Berverage berverage;
public Mocha(Berverage berverage) {
this.berverage = berverage;
}
@Override
public String getDescription() {
return berverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return berverage.cost() + 2; //Mocha 2元
}
}
//装饰者:豆浆
public class Soy implements CondimentDecorator {
private Berverage berverage;
public Soy(Berverage berverage) {
this.berverage = berverage;
}
@Override
public String getDescription() {
return berverage.getDescription() + ", soy";
}
@Override
public double cost() {
return berverage.cost() + 1; //豆浆 1元
}
}
//装饰者:奶盖
public class Whip implements CondimentDecorator {
private Berverage berverage;
public Whip(Berverage berverage) {
this.berverage = berverage;
}
@Override
public String getDescription() {
return berverage.getDescription() + ", Whip";
}
@Override
public double cost() {
return berverage.cost() + 3; //Whip 3元
}
}
运行结果:
public static void main(String[] args) {
//1.买一杯加了奶盖的混合咖啡
Berverage houseBlend = new HouseBlend();
houseBlend = new Whip(houseBlend);
System.out.println(houseBlend.getDescription() + " " + houseBlend.cost());
//2.买一杯加了摩卡和豆浆的深度烘焙
Berverage darkRoast = new DarkRoast();
darkRoast = new Mocha(darkRoast);
darkRoast = new Soy(darkRoast);
System.out.println(darkRoast.getDescription() + " "+darkRoast.cost());
}
扩展:
java i/o中就大量使用装饰模式,比如FileInputStream可以被BufferedInputStream、LineNumberInputStream逐级装饰。有兴趣还可以去看看这些类的实现