zoukankan      html  css  js  c++  java
  • JAVA设计模式之装饰者模式

    咖啡店需要做一个订单系统,以合乎饮料供应要求。

    1.最初是这样设计的:

     1 /**
     2  * 饮料抽象类
     3  *
     4  */
     5 public abstract class Beverage {
     6     
     7     protected String description;
     8     
     9     public String getDescription() {
    10         return this.description;
    11     }
    12     
    13     /**
    14      * 子类需自定义自己的价格
    15      * @return
    16      */
    17     public abstract double cost();
    18     
    19 }

    每一种饮料都需要继承该抽象类,并覆写cost()方法。

    2.但是购买咖啡时需要考虑到调料的部分,每种咖啡会加不同种的调料,比如蒸奶、豆浆、摩卡或者覆盖奶泡,那么订单系统需要考虑加入不同调料后的价格。因此需要实现不同的子类来定义添加不同调料后的价格。大家知道,一种咖啡跟多种调料有多种组合方式,那么多种咖啡和多种调料的组合后,几乎是类爆炸!

    维护极其困难:

    如果某种饮料价格调整;或是新增了某种饮料,怎么办?

    后来经过改进后,把是否存在某种调料作为饮料的属性,并在饮料抽象类中实现cost方法,子类可以覆写cost方法并设置添加的调料最终确定添加不同调料后的价格:

     1 /**
     2  * 饮料抽象类
     3  *
     4  */
     5 public abstract class Beverage {
     6     
     7     private boolean milk;//牛奶
     8     private boolean soy;//豆浆
     9     private boolean mocha;//摩卡
    10     private boolean whip;//奶泡
    11     
    12     private double milkCost = 0.19;
    13     private double soyCost = 0.26;
    14     private double mochaCost = 0.29;
    15     private double whipCost = 0.17;
    16     
    17     protected String description;
    18     
    19     //setter getter method
    20 
    21     public String getDescription() {
    22         return this.description;
    23     }
    24     
    25     public double cost() {
    26         double condimentCost = 0.0;
    27         if (hasMilk()) {
    28             condimentCost += milkCost;
    29         }
    30         if (hasSoy()) {
    31             condimentCost += soyCost;
    32         }
    33         if (hasMocha()) {
    34             condimentCost += mochaCost;
    35         }
    36         if (hasWhip()) {
    37             condimentCost += whipCost;
    38         }
    39         return condimentCost;
    40     }
    41     
    42 }
    43 
    44 /**
    45  * 低糖咖啡
    46  *
    47  */
    48 public class Decaf extends Beverage {
    49     
    50     @Override
    51     public String getDescription() {
    52         return "It is Decaf.";
    53     }
    54 
    55     @Override
    56     public double cost() {
    57         super.setMilk(true);//添加牛奶调料
    58         return 1.99 + super.cost();
    59     }
    60     
    61 }

    这样一来,如果有五种咖啡,那么只需要实现五个子类即可,不同的子类可以灵活设置添加不同的调料。

    但是这样的设计存在一定的问题:

    1)调料价格的改变会使我们改变现有代码;

    2)出现新调料,就需要加上新的方法,并改变父类中的cost方法;

    3)若出现新的饮料,如红茶,那么新的饮料继承该父类,父类中的调料属性并不合适,如奶泡等;

    ... ...

    设计原则:类应该对扩展开放,对修改关闭。

    装饰者模式思想:以饮料为主体,然后在运行时以调料来“装饰”饮料。

    例如客户需要摩卡和奶泡深焙咖啡,那么要做的是:

    拿一个深焙咖啡对象;

    以摩卡对象装饰;

    以奶泡对象装饰;

    摩卡和奶泡属于调料,但是也是装饰者,它的类型反映了它装饰的对象,所谓反映,指的是两者类型一致。那么所有调料需要继承Beverage。

    使用装饰者模式设计的代码:

     1 /**
     2  * 饮料抽象类
     3  *
     4  */
     5 public abstract class Beverage {
     6     
     7     protected String description;
     8     
     9     public String getDescription() {
    10         return this.description;
    11     }
    12     
    13     /**
    14      * 获取每种饮料的价格
    15      * @return
    16      */
    17     public abstract double cost();
    18 }
    19 
    20 /**
    21  * 调料抽象类
    22  *
    23  */
    24 public abstract class Condiment extends Beverage {
    25     
    26     public abstract String getDescription();
    27     
    28 }

    这里调料继承饮料,仅仅是为了使两者具有相同的类型,并非为了复用父类的行为。

    下面是饮料的子类:

     1 /**
     2  * 深焙咖啡
     3  *
     4  */
     5 public class DarkRoast extends Beverage {
     6     
     7     public DarkRoast() {
     8         description = "DarkRoast";
     9     }
    10     @Override
    11     public double cost() {
    12         return 0.19;
    13     }
    14 
    15 }
    16 
    17 /**
    18  * 浓缩咖啡
    19  *
    20  */
    21 public class Espresso extends Beverage {
    22     
    23     public Espresso() {
    24         description = "Espresso";
    25     }
    26     
    27     @Override
    28     public double cost() {
    29         return 1.99;
    30     }
    31 
    32 }
    33 
    34 /**
    35  * 黑咖啡
    36  *
    37  */
    38 public class HoseBlend extends Beverage {
    39     
    40     public HoseBlend() {
    41         description = "Hose Blend Coffee";
    42     }
    43     
    44     @Override
    45     public double cost() {
    46         return 0.99;
    47     }
    48 
    49 }

    调料(装饰者)子类:

     1 /**
     2  * 摩卡
     3  *
     4  */
     5 public class Mocha extends Condiment {
     6 
     7     private Beverage beverage;
     8     
     9     public Mocha(Beverage beverage) {
    10         this.beverage = beverage;
    11     }
    12 
    13     @Override
    14     public String getDescription() {
    15         return beverage.getDescription() + ", Mocha";
    16     }
    17 
    18     @Override
    19     public double cost() {
    20         return 0.20 + beverage.cost();
    21     }
    22 
    23 }
    24 
    25 /**
    26  * 豆浆
    27  *
    28  */
    29 public class Soy extends Condiment {
    30     
    31     private Beverage beverage;
    32     
    33     public Soy(Beverage beverage) {
    34         this.beverage = beverage;
    35     }
    36 
    37     @Override
    38     public String getDescription() {
    39         return beverage.getDescription() + ", Soy";
    40     }
    41 
    42     @Override
    43     public double cost() {
    44         return 0.23 + beverage.cost();
    45     }
    46 
    47 }
    48 
    49 /**
    50  * 奶泡
    51  *
    52  */
    53 public class Whip extends Condiment {
    54     
    55     private Beverage beverage;
    56     
    57     public Whip(Beverage beverage) {
    58         this.beverage = beverage;
    59     }
    60     
    61     @Override
    62     public String getDescription() {
    63         return beverage.getDescription() + ", Whip";
    64     }
    65 
    66     @Override
    67     public double cost() {
    68         return 0.69 + beverage.cost();
    69     }
    70 
    71 }

    测试代码:

     1 public class ComponentTest {
     2     @Test
     3     public void test() {
     4         Beverage beverage = new Espresso();
     5         System.out.println(beverage.getDescription() + ", $" + beverage.cost());
     6         Beverage beverage2 = new HoseBlend();
     7         beverage2 = new Mocha(beverage2);
     8         beverage2 = new Mocha(beverage2);
     9         beverage2 = new Whip(beverage2);
    10         System.out.println(beverage2.getDescription() + ", $" + beverage2.cost());
    11         Beverage beverage3 = new DarkRoast();
    12         beverage3 = new Soy(beverage3);
    13         beverage3 = new Mocha(beverage3);
    14         beverage3 = new Whip(beverage3);
    15         System.out.println(beverage3.getDescription() + ", $" + beverage3.cost());
    16     }
    17 }

    运行结果:

    1 Espresso, $1.99
    2 Hose Blend Coffee, Mocha, Mocha, Whip, $2.08
    3 DarkRoast, Soy, Mocha, Whip, $1.31

    java/IO中有很多用到装饰者模式的设计,有兴趣的朋友可以了解下。

  • 相关阅读:
    Scala篇:Scala环境及IDEA配置
    大数据篇:Hive
    大数据篇:Flume
    大数据篇:Kafka
    15.百万考生成绩如何排序
    Tomcat 架构原理解析到架构设计借鉴
    服务设计思考:平台化
    Druid:通过 Kafka 加载流数据
    12.分而治之-归并排序
    11.经典O(n²)比较型排序算法
  • 原文地址:https://www.cnblogs.com/bearduncle/p/8110253.html
Copyright © 2011-2022 走看看