zoukankan      html  css  js  c++  java
  • 【设计模式

     1、模式简介

      装饰者模式允许向一个现有的对象添加新的功能,同时又不改变其结构。

      装饰者模式的思路是用“调料”对象将原始对象进行层层包裹,同时其属性、动作层层传递,达到最终的扩展效果。因此,要求原始对象和“调料”对象继承同一个类或实现同一个接口,这样才能在装饰完成后调用原始对象的属性或动作。

    装饰者模式的优点:

    • 可以动态的扩展功能;
    • 装饰者和被装饰者解耦,互相不关联。

    装饰者模式的缺点:

      多层装饰比较复杂。

    装饰者模式的适用场景:

    • 扩展一个类的功能;
    • 动态增加和撤销功能。

    注意:装饰者模式可以替代繁杂的继承,但其内部实现使用的也是继承。也就是说,装饰者模式将繁杂的继承转化成了其内部的简单的继承。

    2、案例

    2.1、需求

      一个咖啡店中提供四种咖啡:综合咖啡(HouseBlend)、深焙咖啡(Dark Roast)、低咖啡因咖啡(Espresso)和浓缩咖啡(Decaf)。

      消费者除了可以买这些已经调配好的咖啡之外,还可以添加自己喜欢的调料。供选择的调料有:牛奶(Milk)、巧克力摩卡(Mocha)、豆浆(Soy)和奶泡(Whip)。

      每种咖啡或调料都有自己的价钱,消费者通过选择不同的咖啡和调料进行组合,最终得到最终组合的详细信息(咖啡和调料的名称)以及最终的价钱。

    2.2、分析

      通过描述,我们的程序中需要有两类东西:咖啡和调料,这两种东西可以任意的搭配。针对这个问题我们有如下两种方案:

    方案一:

      我们可以针对每一种可能有的选择编写一个类,如:加一份摩卡的综合咖啡、加两份豆浆和一份奶泡的低咖啡因咖啡,等等。这样做的结果是我们会面临一个非常严重的问题——类爆炸。而且,如果牛奶的价钱上调了,我们也不好维护,因此这种方案是不可行的。

    方案二:

      我们可以使用设计模式中的装饰者模式,将咖啡使用调料层层包裹,最终得到消费者想要的口味搭配。这样,我们就只需要四种咖啡和四种调料这8个类就足够了。

      上面说到,如果使用装饰者模式,那么装饰者和被装饰者都需要继承自同一个父类或实现同一个接口,便于层层包裹。我们这里让这8个类都继承自一个Beverage的抽象类,同时四种调料继承一个继承自Beverage类的装饰者类CondimentDecorator。具体UML图如下所示:

    2.3、实现

      Beverage抽象类:

    public abstract class Beverage {
       protected String description;
     
       public String getDescription() {
          return description;
       }
     
       public abstract double getCost();
    }

      HouseBlend类:

    public class HouseBlend extends Beverage {
     
       public HouseBlend() {
          super.description = "HouseBlend Coffee";
       }
     
       @Override
       public double getCost() {
          return 20.5;
       }
    }

      Milk中的代码:

    public class Milk extends CondimentDecorator {
       private Beverage beverage;
     
       public Milk(Beverage beverage) {
          this.beverage = beverage;
       }
     
       @Override
       public String getDescription() {
          return beverage.getDescription() + " + Milk";
       }
     
       @Override
       public double getCost() {
          return beverage.getCost() + 5.5;
       }
    }

      Test类:

    public class Test {
       public static void main(String[] args) {
          // 一杯低咖啡因咖啡
          Beverage beverage1 = new Espresso();
          System.out.println(beverage1.getDescription() + " = " + beverage1.getCost());
          // 一杯浓缩咖啡,加两份摩卡
          Beverage beverage2 = new Decaf();
          beverage2 = new Mocha(beverage2);
          beverage2 = new Mocha(beverage2);
          System.out.println(beverage2.getDescription() + " = " + beverage2.getCost());
          // 一杯综合咖啡,加一份牛奶和一份摩卡
          Beverage beverage3 = new HouseBlend();
          beverage3 = new Milk(beverage3);
          beverage3 = new Mocha(beverage3);
          System.out.println(beverage3.getDescription() + " = " + beverage3.getCost());
       }
    }

      运行结果如下图所示:

      最后贴出装饰者模式的GitHub代码:【GitHub - Decorator】

  • 相关阅读:
    mysql联合主键,也就是两个数据字段一起做主键的情况
    PHP细节,empty,is_null,isset,if()
    PHP细节,PHP手册中常见的一句话:该函数是二进制安全的
    git和github的学习
    用WPS查看两篇word文档异同之处
    js全角字符转为半角字符
    坑(十七)—— Linux无法挂载NTFS格式的U盘
    subprocess模块
    吴裕雄--天生自然--Go 语言学习笔记--Go 语言数组
    吴裕雄--天生自然--Go 语言学习笔记--Go 语言变量作用域
  • 原文地址:https://www.cnblogs.com/itgungnir/p/6211126.html
Copyright © 2011-2022 走看看