zoukankan      html  css  js  c++  java
  • 装饰者模式--《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的设计大致差不多。

  • 相关阅读:
    September 17th 2016 Week 38th Saturday
    【2016-09-16】UbuntuServer14.04或更高版本安装问题记录
    September 16th 2016 Week 38th Friday
    September 11th 2016 Week 38th Sunday
    September 12th 2016 Week 38th Monday
    September 10th 2016 Week 37th Saturday
    September 9th 2016 Week 37th Friday
    c++暂停
    八皇后问题
    ( 转转)Android初级开发第九讲--Intent最全用法(打开文件跳转页面等)
  • 原文地址:https://www.cnblogs.com/longshaohang/p/3355977.html
Copyright © 2011-2022 走看看