你的位置:首页 > ASP.net教程

[ASP.net教程]第三章:装饰器模式

这章用星巴克咖啡店的例子演示了装饰器模式的使用。

先来看看在星巴克点咖啡的场景:在星巴克你先要点一种饮料,然后你可以加入各种调料(调料也要钱)。

比如:来一份综合咖啡(House Blend),加一份摩卡,再加一份豆浆。那么一共0.89+0.2+0.15=1.24美元。

在比如:来一份综合咖啡,加两份摩卡(我太喜欢摩卡了),再加一份奶泡。

image

 

下面用装饰模式来实现

调料可以看做一种装饰器,装饰在饮料上面。下面是装饰器模式实现星巴克的UML图。

考虑下面的问题:

  • 令人奇怪的是为什么调料要继承自饮料,装饰器模式必须要求装饰器是被装饰者的子类。你现在肯定对此很不解,下文贴出了main函数代码,仔细阅读main函数你应该能明白为什么。
  • 为什么装饰器要有beverage字段?即,为什么装饰器要记住自己装饰的是哪个对象?

image

先看看客户端生产咖啡的代码。真的是太巧妙了,你可以任意添加各种调料。

public class Client {  public static void main(String[] args) {    //综合咖啡 + 摩卡 + 豆浆    Beverage beverage1 = new HouseBlend();    beverage1 = new Mocha(beverage1); //你看,如果调料不是饮料的子类,该语句就出错了     beverage1 = new Soy(beverage1);    System.out.println(beverage1.getDescription() + " $:" + beverage1.cost());        //综合咖啡 + 摩卡 + 摩卡 + 奶泡    Beverage beverage2 = new HouseBlend();    beverage2 = new Mocha(beverage2);    beverage2 = new Mocha(beverage2);    beverage2 = new Whip(beverage2);    System.out.println(beverage2.getDescription() + " $:" + beverage2.cost());  }}

输出结果是:

综合咖啡,摩卡  $:1.24
综合咖啡,摩卡  $:1.3900000000000001

实现代码:

//饮料,是抽象类public abstract class Beverage {  protected String description = "不知道是什么饮料";    public String getDescription() {    return description;  }    public abstract double cost();}//调料(装饰器)。要继承自饮料。虽然调料继承自饮料有点奇怪,但装饰器模式必须要这样。public abstract class CondimentDecorator extends Beverage {  Beverage beverage;    public CondimentDecorator(Beverage beverage) {    this.beverage = beverage;      }}//综合咖啡,是一种饮料。public class HouseBlend extends Beverage {  public HouseBlend() {    description = "综合咖啡";  }    @Override  public double cost() {    return 0.89;  }}//摩卡,是一种调料。价格0.2美元。public class Mocha extends CondimentDecorator {  public Mocha(Beverage beverage) {    super(beverage);    this.beverage.description += ",摩卡"; //此处代码易错,见下面的错误代码演示  }    @Override  public String getDescription() {    return this.beverage.getDescription(); //此处代码易错,见下面的错误代码演示  }    @Override  public double cost() {    return beverage.cost() + 0.2;  }}

错误代码演示

//这段代码错在description字段//Whip自己也从基类继承了description字段//beverage也有一个description字段//仔细想想到底应该改变哪个字段的值???public class Whip extends CondimentDecorator {  Beverage beverage;  public Whip(Beverage beverage) {    this.beverage = beverage;    description += ", 奶泡";  }  @Override  public double cost() {    return beverage.cost() + 0.3;  }}

//这段代码改进了上面提到的错误,但是还有一处错误,你仔细想想看吧!//Soy从基类继承了getDescription()方法//beverage也有getDescription()方法//想想我们到底应该调用谁的getDescription()方法???//那么看来Soy必须override getDescription()方法,并在其中调用beverage的getDescription()方法。public class Soy extends CondimentDecorator {  Beverage beverage;  public Soy(Beverage beverage) {    this.beverage = beverage;    this.beverage.description += ", 豆浆";  }  @Override  public double cost() {    return beverage.cost() + 0.1;  }}

扩展练习

如果想把星巴克的咖啡带回办公室享受,那么需要支付打包费用。因为杯子要钱,杯子还分豪华杯子和简陋的杯子。我也不知道杯子有什么区别,也许豪华的可以保温吧- -。我扩展了下面这个类图,你可以用Java实现一下。

另外,也许有人觉得调料是饮料的子类并不奇怪(也有人把调料当饮料喝)。但是下面这个类图杯子竟然是饮料的子类!!!不要怀疑,装饰器模式就是这样的~~~

image