• 装饰器的理解和使用


    一、是什么?作用

    .装饰器模式可以动态的将责任附加到对象上,若要扩展功能, 装饰者提供比继承更有弹性的替代方案

    二、示例

    1. 需求

    有一杯焦糖奶茶要制作, 可以添加不同的配料,每种配料的价格不同,最后计算奶茶的价格

    2. 原来的做法

    1. 原来的做法,第一种最开始想到的, 首先建奶茶基类,然后新建焦糖奶茶类添加该配料的属性, 在价格方法判断是否有改配料,有的话则加上配料的钱。

    2. 改进的第二种方法是,在建立一个奶茶的子类,添加一个List属性,用这个属性来存放不同的配料,在计算价格的方法里循环List循环加上配料的价格

    总结:

    • 第一种方法问题很大,如果以后需求变化回去修改子类的代码, 破坏了开闭原则, 如果搞个双份椰果,写起来很别扭
    • 第二种方法, 面对配料类没问题, 如果需求在加个按大小杯加钱, 就需要修改子类的代码了,破坏了开闭原则

    3. 用了模式的做法

     1. 奶茶基类和子类

    /**
     * 奶茶基类
     */
    public interface MilkTea {
    
        /**
         * 价格
         */
        float cost();
    
        /**
         * 描述
         */
        String desc();
    }
    
    // ===================================
    
    /**
     * 焦糖奶茶类
     */
    public class CaramelMilkTea implements MilkTea {
        @Override
        public float cost() {
            return 10f;
        }
    
        @Override
        public String desc() {
            return "焦糖奶茶!";
        }
    }

    2. 配料基类和子类

     1 /**
     2  * 配料基类
     3  * 配料有很多种, 所以我们抽象出一个基类
     4  */
     5 public abstract class CondimentDecorator implements MilkTea {
     6 
     7     // 将顶层接口以构造参数的方式传递进来
     8     private MilkTea milkTea;
     9 
    10     public CondimentDecorator(MilkTea milkTea) {
    11         this.milkTea = milkTea;
    12     }
    13 
    14     @Override
    15     public float cost() {
    16         return milkTea.cost();
    17     }
    18 
    19     @Override
    20     public String desc() {
    21         return milkTea.desc();
    22     }
    23 }
    24 
    25 // =================
    26 
    27 /**
    28  * 配料子类 - 布丁
    29  */
    30 public class Pudding extends CondimentDecorator {
    31 
    32     public Pudding(MilkTea milkTea) {
    33         super(milkTea);
    34     }
    35 
    36     /**
    37      * 重写描述, 告诉顾客加了布丁
    38      */
    39     @Override
    40     public String desc() {
    41         return "布丁, " + super.desc();
    42     }
    43 
    44     /**
    45      * 重写价格方法, 在原来的价格上添加布丁的钱
    46      */
    47     @Override
    48     public float cost() {
    49         return super.cost() + 0.5f;
    50     }
    51 }

    3. 现在可以向顾客提供奶茶了, 测试类

    /**
     * 测试主类
     */
    public class Main {
    
        public static void main(String[] args) {
            // 买一杯焦糖奶茶什么都不加
            MilkTea milkTea = new CaramelMilkTea();
            System.out.println("描述: " + milkTea.desc() + ", 价格: " + milkTea.cost());
    
            // 用布丁装饰奶茶
            MilkTea milkTea2 = new CaramelMilkTea();
            milkTea2 = new Pudding(milkTea2);
            System.out.println("描述: " + milkTea2.desc() + ", 价格: " + milkTea2.cost());
    
            // 我要一个双份布丁的奶茶, 再用一层布丁装饰奶茶
            MilkTea milkTea3 = new CaramelMilkTea();
            milkTea3 = new Pudding(new Pudding(milkTea3));
            System.out.println("描述: " + milkTea3.desc() + ", 价格: " + milkTea3.cost());
    
            // todo 如果以后再添加一个需求 每种奶茶有大杯, 中杯, 小杯需要加不同的钱, 只要新建一组杯子基类/ZI类, 重写描述和价格方法就可以了
        }
    }

    控制台显示

      描述: 焦糖奶茶!, 价格: 10.0
      描述: 布丁, 焦糖奶茶!, 价格: 10.5
      描述: 布丁, 布丁, 焦糖奶茶!, 价格: 11.0

    代码小结:

    • 装饰着和杯装饰对象有相同的超类
    • 可以用一个或者多个装饰者包装一个对象
    • 装饰者可以在被装饰者的行为之前或者之后,加上自己的行为,以达到特定的目的

    4. 找一个贴近实际的

    1. 在java.io类中一系列的InputStream类

    三、总结

    1. 首先就是开闭原则,对扩展开放,对修改关闭

    2. 组合和委托可用于在运行时动态的加上新的行为

  • 相关阅读:
    ASP.NET CORE MVC验证码
    高效工作必备黑科技软件和网站
    asp.net core 新建area使用asp-action,asp-controller不管用
    add-migration : 无法将“add-migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次
    entity framework core + SQLite Error 1: 'no such table: Blogs'.
    abp去掉AbpUser中的Name,Surname
    PIE SDK去相关拉伸
    PIE SDK频率域滤波
    PIE SDK均值滤波
    PIE SDK聚类
  • 原文地址:https://www.cnblogs.com/milicool/p/11170526.html
Copyright © 2020-2023  润新知