![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiATN381dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CO0IzMyIDNmZDNmN2MwIWOxYzX3IzM0ATMyIzLcFTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
咖啡类的设计和价格的计算
今天的问题是来自Starbuzz咖啡店(手动狗头),咖啡店里的订单系统是根据一个Beverage类,所有种类的饮品都是继承自这个Beverage类(饮料)。这还不是最头疼的问题,由于每杯饮品消费者可以选择添加不同的调料,如豆浆、摩卡、奶泡等,因此不同的调料组合起来,计算价格成了一个复杂的问题。
你该怎么设计整个订单系统呢?
First Try
在Beverage类中增加调料属性,通过函数判断是否存在某种调料。
每个子类继承Beverage类,重新实现cost方法,计算该种饮料与调料的价格。如下图所示:
这样做似乎很合理,但是仔细思考一下,会出现什么问题呢?
最主要的一个问题是,当调料的价格发生改变或者是增加新的调料时,需要对原有的超类进行修改,这是我们在工程设计中应该避免的。
这里就引出了一条新的设计原则:
类应该对扩展开放,对修改关闭。
这种原则被称为开放-关闭原则,可以保护代码原来的部分被修改,但你也要付出一定的代价,过度使用开放-关闭原则会使代码变得复杂,难于理解和维护。
在这里基于开放-关闭原则,就可以使用装饰者模式。
装饰者模式
什么是装饰者模式呢?比如你要一杯摩卡、奶泡深焙咖啡。
则经过一下步骤:
取深焙咖啡
用摩卡对象装饰
用奶泡对象装饰
计算价格
这里的装饰是怎么实现的?让我们通过代码了解一下:
%饮料抽象类
public abstract class Beverage{
String description = "Unknown Beverage";
public String getDescription(){
return description;
}
public abstract double cost();
}
%调料抽象类,继承自饮料类,目的是保持装饰者与被装饰者处于同一类
public abstract class Condiment extends Beverage{
public abstract String getDescription();%所有的饮料必须重新实现该方法
}
在装饰者模式中,需要保持装饰者和被装饰者处于同一类,才能完成后续方法的调用。
有了两个基类,一起去实现一些咖啡与调料吧!
%HouseBlend咖啡
public class HouseBlend extends Beverage{
String description = "House Blend Coffee";
public double cost(){
return .89;
}
}
%实现摩卡调料
public class Mocha extends Condiment{
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
public String getDescription(){
returrn beverage.getDescription()+",Mocha";
}
public double cost(){
return .15+beverage.cost();
}
}
这里出于简洁的目的,我们只写了一种咖啡和调料,其他的同理啦~
现在万事俱备,快来使用看看吧!请写出如何得到一杯加了双倍摩卡的深焙咖啡~
public class StarbuzzCoffee{
public static void main(String args[]){
Beverage beverage = new HouseBlend();
beverage = new Mocha(beverage);%用摩卡装饰
beverage = new Mocha(beverage);%用第二份摩卡装饰
}
}
是不是很简单!这就是装饰者模式,它科学的定义如下: