• 设计模式篇(Java):装饰者模式


    👨‍💻本文专栏:设计模式篇-装饰者模式
    👨‍💻本文简述:装饰者模式的详解以及jdk中的应用
    👨‍💻上一篇文章: 设计模式篇(Java):桥接模式
    👨‍💻有任何问题,都可以私聊我,文章最后有vx名片。感谢支持!
    🦹知道的越多,不知道的越多!!!不能停下学习的脚步

    在这里插入图片描述

    十、装饰者模式

    10.1 装饰者模式基本介绍

    引出装饰者模式的示例

    咖啡吧订单问题:

    • 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式 咖啡)、Decaf(无因咖啡)
    • 调料:Milk、Soy(豆浆)、Chocolate
    • 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
    • 使用OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合

    那么使用传统方法(最笨的)

    在这里插入图片描述

    问题分析

    • Drink 是一个抽象类,表示饮料
    • des就是对咖啡的描述, 比如咖啡的名字
    • cost() 方法就是计算费用,Drink 类中做成一个抽象方法
    • Decaf 就是单品咖啡, 继承Drink, 并实现cost
    • Espress && Milk 就是单品咖啡+调料, 这个组合很多
    • 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料, 类的数量就会倍增,就会出现类爆炸

    优化方法一

    前面分析到方案1因为咖啡单品+调料 组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目的维护性。

    在这里插入图片描述

    问题分析

    • 有效的缓解了类爆炸的问题
    • 但是在扩展的时候(新增或修改配料的时候)代码维护量过大,不仅要新增类,还需要修改drink

    优化方法二:装饰者模式

    装饰者模式定义

    • 装饰者模式:动态的将新功能附加到对象上在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

    装饰者模式原理

    在这里插入图片描述

    1. 装饰者模式就像打包一个快递
    • 主体:比如:陶瓷、衣服 (Component) // 被装饰者
    • 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
    1. Component 主体:比如类似前面的Drink

    2. ConcreteComponent和Decorator

      1. ConcreteComponent:具体的主体, 比如前面的各个单品咖啡
      2. Decorator: 装饰者,比如各调料
    3. 在如图的Component与ConcreteComponent之间,如果 ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来, 抽象层一个类

    10.2 装饰者模式解决需求

    使用装饰者模式设计的方案

    在这里插入图片描述

    说明

    • Drink 类就是前面说的抽象类, Component
    • ACoffee 就单品咖啡
    • Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)
    • Decorator 的cost 方法进行一个费用的叠加计算,递归的计算价格

    代码示例

    Drink类

    /**
     * Drink类就是抽象的被装饰者,给具体的装饰者和被修饰者继承
     * @author cVzhanshi
     * @create 2023-09-05 15:16
     */
    @Data
    public abstract class Drink {
        // 描述
        private String des;
    
        // 价格
        private float price = 0.0f;
    
        // 计算费用的抽象方法
        // 子类来实现
        public abstract float cost();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Coffee类(缓冲层)

    /**
     * 缓冲层
     * @author cVzhanshi
     * @create 2023-09-05 15:32
     */
    public class Coffee extends Drink {
        @Override
        public float cost() {
            return super.getPrice();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    具体的被装饰者——各种咖啡

    /**
     * @author cVzhanshi
     * @create 2023-09-05 15:33
     */
    public class BCoffee extends Coffee {
        public BCoffee() {
            setDes("BCoffee");
            setPrice(3.0f);
        }
    }
    
    /**
     * @author cVzhanshi
     * @create 2023-09-05 15:33
     */
    public class ACoffee extends Coffee {
        public ACoffee() {
            setDes("ACoffee");
            setPrice(5.0f);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Decorator装饰类

    /**
     * Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)
     * @author cVzhanshi
     * @create 2023-09-05 15:59
     */
    public class Decorator extends Drink{
    
        private Drink coffee;
    
        public Decorator(Drink coffee) {
            this.coffee = coffee;
        }
    
        @Override
        public float cost() {
            // 调料的价格 + coffee的总价格
            return coffee.cost() + super.getPrice();
        }
    
        @Override
        public String getDes() {
            return super.getDes() + " || " + coffee.getDes();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    具体的装饰类

    /**
     * @author cVzhanshi
     * @create 2023-09-05 16:09
     */
    public class Milk extends Decorator {
        public Milk(Drink coffee) {
            super(coffee);
            setPrice(1.0f);
            setDes(" 牛奶 ");
        }
    }
    
    /**
     * @author cVzhanshi
     * @create 2023-09-05 16:10
     */
    public class Soy extends Decorator {
        public Soy(Drink coffee) {
            super(coffee);
            setDes(" 糖 ");
            setPrice(9.0f);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    制作咖啡(允许效果)

    /**
     * @author cVzhanshi
     * @create 2023-09-05 16:11
     */
    public class CoffeeBar {
        public static void main(String[] args) {
            // 装饰者模式下订一份A咖啡 + 一份糖 + 两份牛奶
    
            // 1、点一份a咖啡
            Drink aCoffee = new ACoffee();
    
            System.out.println("费用1 = " + aCoffee.cost());
            System.out.println("描述 = " + aCoffee.getDes());
    
            // 2、a咖啡加入一份糖
            aCoffee = new Soy(aCoffee);
    
            System.out.println("aCoffee 加入一份糖 费用 = " + aCoffee.cost());
            System.out.println("aCoffee 加入一份糖 描述 = " + aCoffee.getDes());
    
            //2、再加入两份份牛奶
            aCoffee = new Milk(aCoffee);
            System.out.println("aCoffee 加入一份糖 一份牛奶 费用 = " + aCoffee.cost());
            System.out.println("aCoffee 加入一份糖 一份牛奶 描述 = " + aCoffee.getDes());
            aCoffee = new Milk(aCoffee);
            System.out.println("aCoffee 加入一份糖 两份牛奶 费用 = " + aCoffee.cost());
            System.out.println("aCoffee 加入一份糖 两份牛奶 描述 = " + aCoffee.getDes());
        }
    }
    
    //   允许效果
    //        费用1 = 5.0
    //        描述 = ACoffee
    //        aCoffee 加入一份糖 费用 = 14.0
    //        aCoffee 加入一份糖 描述 =  糖  || ACoffee
    //        aCoffee 加入一份糖 一份牛奶 费用 = 15.0
    //        aCoffee 加入一份糖 一份牛奶 描述 =  牛奶  ||  糖  || ACoffee
    //        aCoffee 加入一份糖 两份牛奶 费用 = 16.0
    //        aCoffee 加入一份糖 两份牛奶 描述 =  牛奶  ||  牛奶  ||  糖  || ACoffee
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    10.3 装饰者模式在JDK中的应用

    通过阅读源码可知jdk中的io结构就是一个很典型的装饰者模式

    结构如下:
    在这里插入图片描述

    1. InputStream 是抽象类,类似我们前面讲的 Drink
    public abstract class InputStream implements Closeable {
      // ....
    }
    
    • 1
    • 2
    • 3
    1. FilelnputStrearm 是 InputStream 子类,是具体的被装饰者类似我们前面的ACoffee、BCofee

    2. FilterInputStream 是InputStream 子类:类似我们前面的 Decorator 修饰者

    public class FilterInputStream extends InputStream {
      /**
      FilterinputStream 类有 protected volatile InputStream in;即含被装饰者 
       * The input stream to be filtered.
       */
      protected volatile InputStream in;
      // ...
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. DatalnputStream 是 FilterinputStrcam 子类,具体的修饰者,类似前面的 Milk,Soy 等
  • 相关阅读:
    洛谷P3130 Counting Haybale P
    数据结构-快速排序-C语言实现
    docker使用入门
    Linux shell 脚本中的$$、$#、$?、$1的具体含义是什么呢?
    Linux简单使用的服务器日常巡检脚本
    使用Spring Boot整合定时任务(Schedule)
    lv5 嵌入式开发-7 有名管道和无名管道
    Zigbee组网控制流程
    【Try to Hack】HFish蜜罐部署
    Sora 使用教程,新手小白可用
  • 原文地址:https://blog.csdn.net/qq_45408390/article/details/132717965