「聊设计模式」之装饰器模式(Decorator)

举报
bug菌 发表于 2023/09/25 17:51:35 2023/09/25
【摘要】 装饰器模式是指在不改变原有对象的基础上,通过对其进行包装拓展,使其具有更强的功能。它在不改变原有代码的基础上,通过添加一些新的功能来满足用户的需求。


🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!


大家下午好,我是bug菌,今天我们继续聊设计模式。

前言

  设计模式是面向对象编程中的重要概念,它是一种解决特定问题的经过验证的方法。设计模式可以提高代码的复用性、可读性、可维护性和可扩展性。其中,装饰器模式是一种常见的结构型模式。

摘要

  装饰器模式是指在不改变原有对象的基础上,通过对其进行包装拓展,使其具有更强的功能。它在不改变原有代码的基础上,通过添加一些新的功能来满足用户的需求。

装饰器模式

概念

  装饰器模式(Decorator)是一种结构型设计模式,它允许我们在运行时扩展一个对象的功能,而不需要修改它的结构。

  在装饰器模式中,我们创建一个装饰器类,该类实现一个与被装饰对象相同的接口,并且通过将被装饰对象作为参数传递到装饰器中,实现对被装饰对象的包装。这使得我们可以在不修改原始对象的基础上,在运行时动态地添加或删除对象的功能。

  装饰器模式的主要优点是它提供了一种灵活的方式来扩展对象的功能,而不需要修改其结构或代码。此外,它也允许我们避免创建大量的子类来实现对象的不同功能。

  装饰器模式的一些常见应用包括:为对象添加缓存、日志记录、安全验证、性能优化等功能。

结构

装饰器模式包含以下关键元素:

  1. Component(组件):定义装饰器和被装饰器的共同接口,即原始对象的接口。

  2. Concrete Component(具体组件):实现Component接口的具体对象,即被装饰器的原始对象。

  3. Decorator(装饰器):实现Component接口,并包含一个Component类型的成员变量,即被装饰器对象,用于给对象添加新功能。

  4. Concrete Decorator(具体装饰器):实现Decorator接口的具体类,用于给被装饰器对象添加新的行为。

如下是装饰器模式的UML类图:

image.png

优缺点

优点

装饰器模式的优点包括:

  1. 装饰器模式可以动态地添加对象的功能,而无需修改已经存在的代码,从而实现对对象功能的扩展。

  2. 装饰器可以嵌套使用,通过不同的装饰器组合,可以实现各种复杂的功能组合。

  3. 装饰器模式符合“单一职责原则”,将每个装饰器限定于单一的功能范围,使得装饰器更加灵活、易于维护。

缺点

装饰器模式的缺点包括:

  1. 多层的装饰器嵌套会导致系统复杂度增加,调试和维护困难。

  2. 装饰器模式增加了系统的运行时开销,可能会影响系统的性能。

  3. 对象的使用方式变得复杂,需要使用者了解每个装饰器的作用才能正确地使用对象。

应用场景

装饰器模式通常适用于以下场景:

  1. 扩展一个类的功能,而不影响其他对象。
  2. 需要动态添加或移除对象的行为,而不影响其他对象。
  3. 当不能采用子类继承方式进行扩展时,例如在运行时刻需要对对象进行动态扩展。
  4. 当需要对一个对象的多个功能进行排列组合时,可以使用多个不同的装饰器进行组合,从而实现各种功能的自由组合。

例如,一个文本编辑器可以使用装饰器模式来实现对文本的各种格式(如颜色、字体、大小、行距等等)进行动态添加或移除,而不必为每种格式创建一个新的子类。又如,一个咖啡店可以使用装饰器模式来实现多种口味的咖啡的自由组合。

模式实现

  我们以Java开发语言为例来讲解装饰器模式。在这里,我们定义一个接口Component,它有一个方法operation(),用于返回一个字符串。然后,我们定义一个类ConcreteComponent,它实现了Component接口,并重写了operation()方法。最后,我们定义一个抽象类Decorator,它也实现了Component接口,并重写了operation()方法。

Component接口

package com.example.javaDesignPattern.decorator;

/**
 * @author bug菌
 * @version 1.0
 * @date 2023/9/19 14:48
 */
public interface Component {
    public String operation();
}

ConcreteComponent类

package com.example.javaDesignPattern.decorator;

/**
 * @author bug菌
 * @version 1.0
 * @date 2023/9/19 14:50
 */
public class ConcreteComponent implements Component {
    @Override
    public String operation() {
        return "ConcreteComponent";
    }
}

Decorator抽象类

package com.example.javaDesignPattern.decorator;

/**
 * @author bug菌
 * @version 1.0
 * @date 2023/9/19 14:50
 */
public abstract class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public String operation() {
        return component.operation();
    }
}

  在这里,我们定义了一个抽象类Decorator,它有一个Component成员变量,并在构造函数中进行初始化。Decorator类重写了Component接口中的operation()方法,该方法返回成员变量componentoperation()方法的返回值。

  接下来,我们可以定义一些具体的装饰器类,它们继承自Decorator类,并在构造函数中调用父类的构造函数。这些具体的装饰器类可以增加一些新的功能,比如说添加一个字符串“DecoratorA”等。下面是一个具体的装饰器类DecoratorA

package com.example.javaDesignPattern.decorator;

/**
 * 具体的装饰器类DecoratorA
 *
 * @author bug菌
 * @version 1.0
 * @date 2023/9/19 14:50
 */
public class DecoratorA extends Decorator {
    public DecoratorA(Component component) {
        super(component);
    }

    @Override
    public String operation() {
        return "DecoratorA " + component.operation();
    }
}

  在DecoratorA类中,我们重写了operation()方法,并在其中添加了一个字符串“DecoratorA”。同时,我们调用了父类的构造函数,使其具有Decorator类的特性。

测试用例

  我们可以编写一个测试类DecoratorTest,用于测试装饰器模式的实现。在这个测试类中,我们先创建一个ConcreteComponent对象,然后用DecoratorA对其进行包装装饰,并调用operation()方法输出结果。下面是测试代码的完整实现:

package com.example.javaDesignPattern.decorator;

/**
 * @author bug菌
 * @version 1.0
 * @date 2023/9/19 14:51
 */
public class DecoratorTest {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component decoratedComponent = new DecoratorA(component);
        System.out.println(decoratedComponent.operation());
    }
}

  在这个测试类中,我们创建了一个ConcreteComponent对象,并将其传递给DecoratorA类的构造函数中。最后,我们调用了DecoratedComponent对象的operation()方法,并输出了结果。

运行结果

执行结果如下:

image.png

  当我们运行DecoratorTest类时,可以看到控制台输出了“DecoratorA ConcreteComponent”的字符串。这说明我们成功地实现了装饰器模式。

代码解析

  首先定义了一个抽象组件(Component)接口,它有一个操作方法operation()

  接着定义了具体组件(ConcreteComponent)类,实现了抽象组件接口,并实现了operation()方法。

  然后定义了装饰器(Decorator)类,也是实现了抽象组件接口,并包含了一个抽象组件对象作为成员变量,用于装饰(增强)抽象组件对象的操作方法。

  最后定义了具体装饰器A(DecoratorA)类,它继承了装饰器类,并实现了其对抽象组件对象的具体装饰行为。

  在main函数中,首先创建了一个具体组件对象,然后创建了一个具体装饰器A对象,用于装饰具体组件对象,最后调用了装饰后的组件对象的操作方法。

  此时,输出的结果就是具体装饰器A对具体组件对象操作后的结果。

小结

  在本篇文章中,我们详细介绍了装饰器模式的概念及其实现方式。装饰器模式可以为现有对象添加一些新的功能,而不需要修改原有的代码。这种设计模式可以提高代码的可读性、可维护性和可扩展性,因此在实际开发中得到了广泛的应用。

附录源码

  如上涉及代码均已上传同步在 GitHub,提供给同学们参考性学习。

总结

  在本篇文章中,我们讲解了装饰器模式的实现方式及其应用场景。在实际开发中,装饰器模式可以为现有对象添加一些新的功能,而不需要修改原有的代码。这种设计模式可以提高代码的可读性、可维护性和可扩展性,因此在实际开发中得到了广泛的应用。同时,在编写代码时,我们也需要注意装饰器类的数量,不能过多地添加,否则会导致代码的臃肿和复杂度的提高。

☀️建议/推荐你


  如果想系统性的全面学习设计模式,建议小伙伴们直接毫无顾忌的关注这个专栏《聊设计模式》,无论你是想提升自己的编程技术,还是渴望更好地理解代码背后的设计思想,本专栏都会为你提供实用的知识和启发,帮助你更好地解决日常开发中的挑战,将代码变得更加优雅、灵活和可维护!

  最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

  同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。

📣关于我

  我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。