• 每日一个设计模式之【装饰器模式】


    每日一个设计模式之【装饰器模式

    ☁️前言🎉🎉🎉

      大家好✋,我是知识汲取者😄,今天给大家带来一篇有关装饰器模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性可移植性可维护性,同时还能让你的代码更加精炼,具备艺术美感

      俗话说”靠衣装,佛靠金装“,在代码的世界也是这样的,对象也需要通过穿“衣服”让对象变得更加“漂亮”,装饰器模式就是一个给对象穿“衣服”的角色,它可以通过给对象添加不同的“衣服”让对象的功能变得更加强大,以此完成更加复杂的功能。现在就让我们一起来学习这么一个有趣的模式吧😃

    学习1

    推荐阅读

    • 设计模式导学:🚪传送门
    • 每日一个设计模式系列专栏:🚪传送门
    • 设计模式专属Gitee仓库:✈启程
    • 设计模式专属Github仓库:🚀上路
    • 知识汲取者的个人主页:💓点我哦

    🌻装饰器模式概述

    • 什么是装饰器模式

      装饰器模式(Decorator Pattern),也称包装器模式(Wrapper),是一种结构型模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构,具有很强的灵活性

    • 装饰器模式的作用:动态1地给一个对象添加一些额外的职责

    • 装饰器模式的优缺点

      • 优点

        • 提高了系统的灵活性。装饰器模式采用了委托机制,就增加新功能它比继承机制更加灵活;并且它还可以动态添加或撤销对象的新功能,这是静态的继承无法比拟的
        • 降低了系统的耦合度。装饰类和被装饰类可以相互独立,每一个装饰类都可以独立变化,与被装饰类关联性不大,很大程度降低了系统的耦合度,这也就能够很好的保障系统的可扩展性、可维护性

        ……

      • 缺点:由于装饰器模式通过具体的装饰类增加对象的功能,这就会导致程序中增加许多功能类似的很小的类,进而增加系统的复杂度

        ……

    • 装饰器模式的适用场景

      • 想要动态地给对象增加一个新的功能,但是又不想改变对象原有的结构
      • 想要扩展一个类的功能时可以使用
      • 如果一个对象需要经过多层包装,并且每层包装的对象都是需要被使用的,可以使用(半透明装饰器模式)

      ……

      生活中的应用:床衣服、戴首饰……凡是需要装饰的物品都是

      Java中的应用:java.io包下的类就是使用装饰器模式实现的,我们可以对FileInputStream进行层层包装

    • 装饰器模式的角色划分

      • 抽象构件(Component):是具体被装饰的对象,内部定义了被装饰物主要行为和属性
      • 具体构件(ConcreteComponent):是具体的被装饰者,是被装饰的实际对象
      • 抽象装饰者(Decorator):该角色是包含一个抽象构件的引用,是装饰器模式中的核心角色,定义了具体构件的主要行为,一般是一个具体的类或者抽象类
      • 具体装饰者(ConcreteDecorator):该角色实现类抽象构件的行为,主要责任是给装饰物添加新的具体功能

      PS:这些角色可以参考下面的案例进行理解

    🌱装饰器模式的实现

    🍀透明装饰器模式

    透明装饰器模式的特点是可以通过层层装饰,构件一个具有复杂功能的对象,但是客户端无法调用具体装饰器特有的功能

    示例

    案例描述:手机分为简单手机、增强版手机、复杂版手机,简单手机在收到来电时只有一个响铃的功能,增强版手机收到来电相较简单手机会增加一个震动功能,复杂版手机相较于增强版手机收到来电时会增加一个闪光功能,现在使用装饰者模式来实现这三款手机

    image-20221114161024248

    温馨提示:通过上面的类图可以发现测试类(这里模拟客户端)交互的类太多了,这是由于在Test中要创建大量的对象,其实这里也可以使用工厂模式进行优化,让测试类只与工厂类进行交互,从而进一步降低耦合度,但这里只是为了体验组合模式就不改造了,感兴趣的读者可以自行改造

    创建抽象构件
    创建具体构件
    创建抽象装饰者
    创建具体装饰者
    编写测试类
    • Step1:创建抽象构件

      package com.hhxy.phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title
       * @description
       */
      public abstract class Phone {
          /**
           * 修饰方法
           */
          public abstract void decorate();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    • Step2:创建具体构件

      1)简单手机:

      package com.hhxy.phone.ext;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title 简单手机
       * @description 只有响铃功能
       */
      public class SimplePhone extends Phone {
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              System.out.println("的简单手机");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

      2)增强版手机

      和简单手机的代码是类似的,详细代码请参考Github或Gitee仓库,略……

      3)复杂版手机

      和简单手机的代码是类似的,详细代码请参考Github或Gitee仓库,略……

    • Step3:创建抽象装饰者

      package com.hhxy.decorator.transparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title
       * @description
       */
      public class Decorator extends Phone{
      
          protected Phone phone;
      
          public Decorator(Phone phone){
              this.phone = phone;
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              phone.decorate();
          }
      }
      
      • 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
    • Step4:创建具体装饰者

      1)添加响铃功能的装饰器

      package com.hhxy.decorator.transparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title 响铃装饰者
       * @description 用于给手机添加响铃功能
       */
      public class BellDecorator extends Decorator {
      
          public BellDecorator(Phone phone) {
              super(phone);
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              this.addBell();
              super.decorate();
          }
      
          /**
           * 添加响铃功能的方法
           */
          private void addBell(){
              System.out.print("响铃");
          }
      }
      
      • 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

      2)添加震动功能的装饰器

      package com.hhxy.decorator.transparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title 震动修饰者
       * @description 用于给手机添加震动功能
       */
      public class ShockDecorator extends Decorator {
      
          public ShockDecorator(Phone phone) {
              super(phone);
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              this.addShock();
              super.decorate();
          }
      
          /**
           * 添加震动的方法
           */
          private void addShock(){
              System.out.print("震动");
          }
      }
      
      • 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

      3)添加闪光功能的装饰器

      package com.hhxy.decorator.transparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title 闪光修饰类
       * @description 用于给手机添加闪光功能
       */
      public class FlashDecorator extends Decorator {
      
          public FlashDecorator(Phone phone) {
              super(phone);
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              this.addFlash();
              super.decorate();
          }
      
          /**
           * 添加闪光的方法
           */
          private void addFlash(){
              System.out.print("闪光");
          }
      }
      
      • 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
    • Step5:编写测试类

      package com.hhxy.test;
      
      import com.hhxy.decorator.transparent.BellDecorator;
      import com.hhxy.decorator.transparent.FlashDecorator;
      import com.hhxy.decorator.transparent.ShockDecorator;
      import com.hhxy.phone.Phone;
      import com.hhxy.phone.ext.ComplexPhone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title 测试类1
       * @description 用于测试透明装饰模式
       */
      public class Test1 {
      
          public static void main(String[] args) {
              //三款不同类型的手机
              Phone phone1,phone2,phone3;
              //
              Phone decorator1,decorator2,decorator3;
      
              //简单手机
              /*phone1 = new SimplePhone();
              decorator1 = new BellDecorator(phone1);
              decorator1.decorate();*/
      
              //增强手机
              /*phone2 = new EnhancePhone();
              decorator1 = new BellDecorator(phone2);
              decorator2 = new ShockDecorator(decorator1);
              decorator2.decorate();*/
      
              //复杂手机
              phone3 = new ComplexPhone();
              decorator1 = new BellDecorator(phone3);
              decorator2 = new ShockDecorator(decorator1);
              decorator3 = new FlashDecorator(decorator2);
              decorator3.decorate();
      
          }
      }
      
      • 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
      • 40
      • 41
      • 42

      测试结果:

      image-20221114105548953

    🍀半透明装饰器模式

    半透明装饰器模式是最常见的装饰器模式,它和对象适配器模式的是十分相似的,不同之处在于适配器模式是兼容接口,让一个都对象能调用另一个对象的方法,而对象适配器是为了让一个对象增加一个新的功能。所以也可以称半透明装饰器模式为版适配器半装饰器模式。

    关于对象适配器模式可以参考这篇文章: 每日一个设计模式之【适配器模式】

    示例

    我们依然以上面一个示例为基准,使用半透明装饰器模式来实现

    image-20221114161859810

    创建抽象构件
    创建具体构件
    创建抽象装饰者
    创建具体装饰者
    编写测试类
    • Step1:创建抽象构件

      和透明装饰器模式的代码一致,略……

    • Step2:创建具体构件

      和透明装饰器模式的代码一致,略……

    • Step3:创建抽象装饰者

      package com.hhxy.decorator.untransparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/10/12
       * @title
       * @description
       */
      public class Decorator extends Phone{
      
          protected Phone phone;
      
          public Decorator(Phone phone){
              this.phone = phone;
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              phone.decorate();
          }
      }
      
      • 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
    • Step4:创建具体装饰者

      1)简单手机装饰者

      package com.hhxy.decorator.untransparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/11/14
       * @title
       * @description
       */
      public class SimpleDecorator extends Decorator{
      
          public SimpleDecorator(Phone phone) {
              super(phone);
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              this.addBell();
              super.decorate();
          }
      
          /**
           * 添加响铃功能
           */
          public void addBell(){
              System.out.print("响铃");
          }
      }
      
      • 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

      2)增强版手机装饰者

      package com.hhxy.decorator.untransparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/11/14
       * @title
       * @description
       */
      public class EnhanceDecorator extends Decorator{
          public EnhanceDecorator(Phone phone) {
              super(phone);
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              this.addBell();
              this.addShock();
              super.decorate();
          }
      
          /**
           * 添加响铃功能
           */
          public void addBell(){
              System.out.print("响铃");
          }
      
          /**
           * 添加震动功能
           */
          public void addShock(){
              System.out.print("震动");
          }
      }
      
      • 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

      3)复杂版手机装饰者

      package com.hhxy.decorator.untransparent;
      
      import com.hhxy.phone.Phone;
      
      /**
       * @author ghp
       * @date 2022/11/14
       * @title
       * @description
       */
      public class ComplexDecorator extends Decorator{
          public ComplexDecorator(Phone phone) {
              super(phone);
          }
      
          /**
           * 修饰方法
           */
          @Override
          public void decorate() {
              this.addBell();
              this.addShock();
              this.addFlash();
              super.decorate();
          }
      
          /**
           * 添加响铃功能
           */
          public void addBell(){
              System.out.print("响铃");
          }
      
          /**
           * 添加震动功能
           */
          public void addShock(){
              System.out.print("震动");
          }
      
          /**
           * 添加闪光功能
           */
          public void addFlash(){
              System.out.print("闪光");
          }
      
          /**
           * 该装饰者类中特有的功能
           */
          public void special(){
              System.out.println("这是ComplexDecorator特有的功能");
          }
      
      }
      
      • 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
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
    • Step5:编写测试类

      package com.hhxy.test;
      
      import com.hhxy.decorator.untransparent.ComplexDecorator;
      import com.hhxy.phone.Phone;
      import com.hhxy.phone.ext.ComplexPhone;
      
      /**
       * @author ghp
       * @date 2022/11/14
       * @title 测试类2
       * @description 用于测试半透明装饰器模式
       */
      public class Test2 {
          public static void main(String[] args) {
              Phone phone = new ComplexPhone();
              ComplexDecorator complexDecorator = new ComplexDecorator(phone);
              complexDecorator.decorate();
              complexDecorator.special();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

      测试结果:

      image-20221114110545895

    🌲总结

    • 透明装饰器模式和半透明装饰器模式的比较:

      • 透明装饰器模式实现更加复杂。他需要将一个类的职责细分成多个类似的装饰类,每个装饰类的装饰顺序是有要求的,同时也要把握细分的度,防止产生大量装饰类,增大系统的复杂度;而半透明装饰类是可以直接将一个类装饰完成,不需要层层装饰
      • 透明装饰器模式更加灵活。透明装饰器模式可以通过层层装饰形成具有不同功能的对象,可变性更多;而半透明装饰器模式无法进行层层装饰,只能一步装饰到位
      • 透明装饰器模式更加安全。透明装饰模式使用多态的写法,符合依据依赖倒置原则,实现了抽象和实现的分离,很大程度屏蔽了外界对于具体实现的了解,让用户能够更加安全地调用装饰类的方法,但是无法调用装饰类的特有方法;而半透明装饰器模式是直接在客户端(也就是Test类中)使用实现对象,而不是抽象对象,让装饰器对象能够调用装饰类的特有方法

      总的而言,装饰器模式核心实现是利用委托机制,相较于传统的继承机制,能够更加灵活地去扩展功能。其中半透明装饰器模式是最常见的装饰器模式,它是介于适配器模式和装饰器模式之间的,而透明装饰器模式是一种理想的装饰器模式,它的设计难度更大。

    自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O

    点赞


    上一篇:每日一个设计模式之【组合模式】

    下一篇:每日一个设计模式之【外观模式】

    参考文章

    在次致谢


    1. 动态:这里的动态是指在代码运行时,对象就多了一个新功能,而在编译阶段对象是不具有该功能,可以类比动态语言的定义 ↩︎

  • 相关阅读:
    网络编程入门
    合并excel方法汇总
    【面向对象】【数组去重】【反转字符串】
    坤坤的悲伤生活
    ARM64汇编入门
    C语言课程设计:学生成绩信息管理系统(排序、平均分、总分)详解
    【80%测试员被骗,关于jmeter 的一个弥天大谎!】
    VS code常用插件
    Ubuntu18.04 系统没有声音输出的解决过程
    Docker导入/导出
  • 原文地址:https://blog.csdn.net/qq_66345100/article/details/127850614