喝咖啡是大多数上班族的习惯,jack作为他们公司的骨干,自然少不了喝咖啡。
在他们公司楼下,有一家叫星巴拉咖啡店,该公司营业有方正以每天两家分店的速度向外扩张。
星巴拉咖啡打算更新一下他们的订单系统,以合乎他们的饮料供应。
一个周六的下午,jack来到了星巴拉咖啡,在柜台前点了一杯拿铁,并要求配料中加两份牛奶,但是它的订单系统并不能支持,只能给他加一份。
好家伙这可给jack急的,店员立马找来了门店经理。
经理告诉jack,他们的订单系统目前比较落后,目前正筹划更新系统呢。
jack看了他们原先的系统类图是这样的
再接着,他们的各种咖啡中还能在其中加入各种调料,例如:牛奶(milk),豆浆(soy),摩卡(mocha)等。会根据所添加的不同调料而收取相应费用。
这是第一个尝试
看了这样的类图简直是类爆炸,每一个咖啡和每一个配料组合一起为一个类,要是配料多起来就要为每一种咖啡都要加一个该配料的类。简直是一个恶梦,难怪无法满足jack加两份牛奶的需求,而且如果牛奶价格上扬的话,他就要修改所有加了牛奶的咖啡类。
于是jack脑子立马又浮现了一个设计,让Beverage咖啡类里包含配料的属性,和他的方法,见类图
但是作为外行人的门店经理,仿佛通灵一般,看出了该系统存在的缺陷。
经理说:“jack啊,咱这个系统要是改了配料的价格的话,就要修改所有的咖啡类了,要是以后还有其他的茶类的话,这些配料像牛奶,摩卡都不能使用的。要是有的顾客挑剔一点的话,要加两份牛奶的话,那也没办法实现啊”
jack说:“老铁,没毛病!”
于是jack又开始头脑风暴了。
此刻,jack面临了一种重要的设计原则,那就是开闭原则
开闭原则:类应该对扩展开放,对修改关闭。我们的目标是允许类容易扩展,在不修改现有代码的情况下,可以添加新的行为。
于是jack想到了装饰者模式,装饰者模式是要先定义一个组件,那就是抽象组建,那就是Beverage,然后是具体的咖啡类都去继承beverage,再定义一个配料类CondimentDecorator,也去继承Beverage,然后具体的配料再去继承CondimentDecorator,这样所有类的父类都是Beverage。配料类中有一个Beverage类属性维护(这是装饰者模式的精华所在,把被装饰的咖啡注入这个属性,能得到原来被装饰时的价格)。
现在我们实现代码
package coffer;
/**
* 这是抽象类,是咖啡和调料的父类
* Created by huangx on 2018/11/15.
*/
public abstract class Beverage {
public abstract String getDescription();
public abstract int cost();
}
package coffer;
package coffer;
/**
* 这是调料装饰者,继承与Beverage
* Created by huangx on 2018/11/15.
*/
public abstract class CondimentDecorator extends Beverage {
Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
public abstract String getDescription();
public abstract int cost();
}
package coffer;
/**
* 这是具体的组件超优深焙咖啡
* Created by huangx on 2018/11/15.
*/
public class DarkRoast extends Beverage {
public String getDescription() {
return "i am darkRoast ";
}
public int cost() {
return 15;
}
}
package coffer;
/**
* 这是具体组件 中优深焙咖啡,我们就实现这两种咖啡,其他类型的拿铁(latter),脱咖啡因咖啡(decaf)也是一样,就不写了
* Created by huangx on 2018/11/15.
*/
public class HouseBlend extends Beverage {
public String getDescription() {
return "i am houseBlend ";
}
public int cost() {
return 12;
}
}
package coffer;
/**
* 具体的装饰者,即调料
* Created by huangx on 2018/11/15.
*/
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
public String getDescription() {
return beverage.getDescription() + "add milk ";
}
public int cost() {
return 5 + beverage.cost();
}
}
package coffer;
/**
* 具体的装饰者,即调料,其他的Soy,whip调料也不写了
* Created by huangx on 2018/11/15.
*/
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
public String getDescription() {
return beverage.getDescription() +"add mocha ";
}
public int cost() {
return 7 + beverage.cost();
}
}
package coffer;
/**
* 测试类
* Created by huangx on 2018/11/15.
*/
public class TestApplication {
public static void main(String[] args) {
Beverage beverage = new Milk(new Mocha(new Mocha(new DarkRoast())));
System.out.println(beverage.getDescription() + "--------- 需要支付" + beverage.cost());
}
}
运行结果:
本例中的装饰者是配料,被装饰的是咖啡。咖啡作为配料的属性进行注入,并由此得到价格,当然可以再用这个装饰后的咖啡,再被装饰一下,又得出了一个新的价格。
bingo,jack终于可以大肆的加牛奶了。老板为了答谢jack的设计,决定每次jack来店点单,都给他的咖啡免费加五份牛奶。