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的设计大致差不多。

  • 相关阅读:
    render()中添加js函数
    切图相关记录
    Jenkins 配置代理。
    Ant 打包 web 项目 xml 模板
    linux 删除命令
    git 创建 本地 裸 仓库
    swagger spring-boot 配置
    Idea 根据 表 生成 实体类
    java 流 api
    spring eureka 注册显示 ip:端口号
  • 原文地址:https://www.cnblogs.com/longshaohang/p/3355977.html
Copyright © 2011-2022 走看看