装饰者模式--《Head First DesignPattern》

装饰者模式动态地将责任附加到对象杭,若要拓展功能,装设置提供了比继承更有弹性的替代方案。

星巴兹有多种咖啡,它们具有不同的价格。在购买咖啡时,也可以要求在其中加入各种调料,例如豆浆、摩卡、奶泡等等。需要根据所加入的调料收取不同的费用。

这里运用”装饰者模式“,以饮料为主体,然后在运行时以调料来“装饰”饮料,比如说顾客想要摩卡和奶泡深焙咖啡,那么

  1. 哪一个深焙咖啡(DarkRoast)对象
  2. 以摩卡对象装饰它
  3. 以奶泡对象装饰它
  4. 调用cost()方法,并依赖委托(delegate)将调料的价格加上去。

看一下装饰者模式的类图是如何实现的:

  1. ConcreteComponent是我们要动态地加上新行为的对象
  2. 每个Component都可以单独地使用,或者被装饰者包起来用
  3. 每个装饰者都包装一个组件,也就是说,装饰者有一个实例变量以保存某个Component的应用。
  4. Decorator是装饰者共同实现的接口。
  5. ConcreteDecorator有一个实例变量,可以记录所装饰的食物。

所以实现的类图就变成:

代码如下:

Beverage,是基类,实现了getDescription(),同时cost是抽象方法。由子类去实现。

 1 package headfirst.decorator.starbuzz;
 2 
 3 public abstract class Beverage {
 4     String description = "Unknown Beverage";
 5   
 6     public String getDescription() {
 7         return description;
 8     }
 9  
10     public abstract double cost();
11 }

四种咖啡:

HouseBlend,DarkRoast,Espresso,Decaf

 1 package headfirst.decorator.starbuzz;
 2 
 3 public class HouseBlend extends Beverage {
 4     public HouseBlend() {
 5         description = "House Blend Coffee";
 6     }
 7  
 8     public double cost() {
 9         return .89;
10     }
11 }
12 
13 public class DarkRoast extends Beverage {
14     public DarkRoast() {
15         description = "Dark Roast Coffee";
16     }
17  
18     public double cost() {
19         return .99;
20     }
21 }
22 
23 public class Espresso extends Beverage {
24   
25     public Espresso() {
26         description = "Espresso";
27     }
28   
29     public double cost() {
30         return 1.99;
31     }
32 }
33 
34 public class Decaf extends Beverage {
35     public Decaf() {
36         description = "Decaf Coffee";
37     }
38  
39     public double cost() {
40         return 1.05;
41     }
42 }

接下来是调料接口,或者是抽象类。它必须继承我们的Beverage。并且声明了getDescription()方法。

1 package headfirst.decorator.starbuzz;
2 
3 public abstract class CondimentDecorator extends Beverage {
4     public abstract String getDescription();
5 }

接下来是四种调料:

 1 package headfirst.decorator.starbuzz;
 2 
 3 public class Mocha extends CondimentDecorator {
 4     Beverage beverage;
 5 
 6     //保存了一个Berverage对象
 7     public Mocha(Beverage beverage) {
 8         this.beverage = beverage;
 9     }
10  
11     public String getDescription() {
12         return beverage.getDescription() + ", Mocha";
13     }
14  
15     public double cost() {
16         //注意这里委托了beverage.cost()
17         return .20 + beverage.cost();
18     }
19 }
20 
21 public class Whip extends CondimentDecorator {
22     Beverage beverage;
23  
24     public Whip(Beverage beverage) {
25         this.beverage = beverage;
26     }
27  
28     public String getDescription() {
29         return beverage.getDescription() + ", Whip";
30     }
31  
32     public double cost() {
33         return .10 + beverage.cost();
34     }
35 }
36 
37 public class Soy extends CondimentDecorator {
38     Beverage beverage;
39 
40     public Soy(Beverage beverage) {
41         this.beverage = beverage;
42     }
43 
44     public String getDescription() {
45         return beverage.getDescription() + ", Soy";
46     }
47 
48     public double cost() {
49         return .15 + beverage.cost();
50     }
51 }
52 
53 public class Milk extends CondimentDecorator {
54     Beverage beverage;
55 
56     public Milk(Beverage beverage) {
57         this.beverage = beverage;
58     }
59 
60     public String getDescription() {
61         return beverage.getDescription() + ", Milk";
62     }
63 
64     public double cost() {
65         return .10 + beverage.cost();
66     }
67 }

最后是测试代码:

 1 package headfirst.decorator.starbuzz;
 2 
 3 public class StarbuzzCoffee {
 4  
 5     public static void main(String args[]) {
 6         Beverage beverage = new Espresso();
 7         System.out.println(beverage.getDescription() 
 8                 + " $" + beverage.cost());
 9  
10         Beverage beverage2 = new DarkRoast();
11         beverage2 = new Mocha(beverage2);
12         beverage2 = new Mocha(beverage2);
13         beverage2 = new Whip(beverage2);
14         System.out.println(beverage2.getDescription() 
15                 + " $" + beverage2.cost());
16  
17         Beverage beverage3 = new HouseBlend();
18         beverage3 = new Soy(beverage3);
19         beverage3 = new Mocha(beverage3);
20         beverage3 = new Whip(beverage3);
21         System.out.println(beverage3.getDescription() 
22                 + " $" + beverage3.cost());
23     }
24 }

那如果我们这时产生了新的需求,要求在菜单上加上咖啡的容量的大小,供顾客选择大杯,小杯,中杯,那该怎么办?要注意,大杯的饮料比较贵,同时它加的调料也要比较多,所以调料的价格也不一样。

这时我们应该在Beverage中定义size和getSize()的函数,并且在四种饮料中要根据size的大小,cost()函数要返回不同的价格。

在调料中,我们也需要获取被装饰者的size,然后cost函数加上对应调料的价格。

 1 package headfirst.decorator.starbuzz;
 2 
 3 public class Soy extends CondimentDecorator {
 4     Beverage beverage;
 5 
 6     public Soy(Beverage beverage) {
 7         this.beverage = beverage;
 8     }
 9 
10     public String getDescription() {
11         return beverage.getDescription() + ", Soy";
12     }
13     
14     public int getSize(){
15         return beverage.getSize();
16     }
17 
18     public double cost() {
19         double cost = beverage.cost();
20         if (getSize() == Beverage.TALL){
21             cost += .10;
22         }else if (getSize() == Beverage.GRANDE){
23             cost += .15;
24         }else if (getSize() == Beverage.VENTI){
25             COST += .20;
26         }
27         return cost;
28     }
29 }

最后要说的就是java.io包中,定义了很多类,就是装饰者模式的实现。我们可以看看InputStream的实现。

OutputStream和Reader,Writer的设计大致差不多。

 

posted @ 2013-10-07 20:42  longshaohang  阅读(452)  评论(0编辑  收藏  举报