天天看点

Head First 设计模式——装饰者模式

Head First 设计模式——装饰者模式

咖啡类的设计和价格的计算

今天的问题是来自Starbuzz咖啡店(手动狗头),咖啡店里的订单系统是根据一个Beverage类,所有种类的饮品都是继承自这个Beverage类(饮料)。这还不是最头疼的问题,由于每杯饮品消费者可以选择添加不同的调料,如豆浆、摩卡、奶泡等,因此不同的调料组合起来,计算价格成了一个复杂的问题。

你该怎么设计整个订单系统呢?

First Try

在Beverage类中增加调料属性,通过函数判断是否存在某种调料。

每个子类继承Beverage类,重新实现cost方法,计算该种饮料与调料的价格。如下图所示:

Head First 设计模式——装饰者模式

这样做似乎很合理,但是仔细思考一下,会出现什么问题呢?

最主要的一个问题是,当调料的价格发生改变或者是增加新的调料时,需要对原有的超类进行修改,这是我们在工程设计中应该避免的。

这里就引出了一条新的设计原则:

类应该对扩展开放,对修改关闭。

这种原则被称为开放-关闭原则,可以保护代码原来的部分被修改,但你也要付出一定的代价,过度使用开放-关闭原则会使代码变得复杂,难于理解和维护。

在这里基于开放-关闭原则,就可以使用装饰者模式。

装饰者模式

什么是装饰者模式呢?比如你要一杯摩卡、奶泡深焙咖啡。

则经过一下步骤:

取深焙咖啡

用摩卡对象装饰

用奶泡对象装饰

计算价格

这里的装饰是怎么实现的?让我们通过代码了解一下:

%饮料抽象类
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);%用第二份摩卡装饰
  }
}
      

是不是很简单!这就是装饰者模式,它科学的定义如下: