装饰器模式:让你的对象变得更强大

news2024/11/26 9:44:45

在日常开发中,当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。这时候,我们该怎么办呢?我们可以使用装饰器器模式来解决这个问题,本文将从以下四个方面讲解装饰器器模式

  • 简介

  • 优缺点

  • 应用场景

  • Java 代码示例、

  • Spring 代码示例

简介

装饰器模式(Decorator Pattern)是一种结构型设计模式,它可以在不改变现有对象的结构的情况下,动态地给对象增加一些额外的功能。装饰器模式通过创建一个包装对象(即装饰器)来包裹真实对象,并在保持真实对象的接口不变的前提下,为其提供额外的功能。装饰器模式可以在运行时根据需要选择不同的装饰器来组合和修改对象的行为。

  • Component(组件接口):所有被装饰组件及装饰器对应的接口标准,指定进行装饰的行为方法。对应本章例程中的展示接口 Showable。

  • ConcreteComponent(组件实现):需要被装饰的组件,实现组件接口标准,只具备自身未被装饰的原始特性。对应本章例程中的女生类 Girl。

  • Decorator(装饰器):装饰器的高层抽象类,同样实现组件接口标准,且包含一个被装饰的组件。

  • ConcreteDecorator(装饰器实现):继承自装饰器抽象类的具体子类装饰器,可以有多种实现,在被装饰组件对象的基础上为其添加新的特性。对应本章例程中的粉底类 FoundationMakeup、口红类 Lipstick。

推荐博主开源的 H5 商城项目waynboot-mall,这是一套全部开源的微商城项目,包含三个项目:运营后台、H5 商城前台和服务端接口。实现了商城所需的首页展示、商品分类、商品详情、商品 sku、分词搜索、购物车、结算下单、支付宝/微信支付、收单评论以及完善的后台管理等一系列功能。 技术上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中间件。分模块设计、简洁易维护,欢迎大家点个 star、关注博主。

github 地址:https://github.com/wayn111/waynboot-mall

优缺点

装饰器模式的优点有:

  • 装饰器模式是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。

  • 通过使用不同的装饰器及这些装饰器的排列组合,可以实现不同效果。

  • 装饰器模式完全遵守开闭原则,可以在不修改原有代码的基础上增加新的功能。

装饰器模式的缺点有:

  • 装饰器模式会增加许多子类,过度使用会增加程序的复杂性。

  • 装饰器模式会增加对象之间的联系,可能会引入循环引用的问题。

  • 装饰器模式会影响对象的标识,当使用装饰器对对象进行包装时,对象的类型和行为可能会发生变化。

应用场景

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

  • 当需要给一个现有的类添加附加职责,而又不能采用继承的方式时,可以使用装饰器模式。例如,在不修改原有代码的情况下给一个窗口添加滚动条或者边框等功能。

  • 当需要动态地给一个对象增加功能,而又需要撤销该功能时,可以使用装饰器模式。例如,在电子商务系统中根据用户选择的不同优惠券来计算商品价格时,可以使用装饰器模式来实现。

  • 当需要为一批兄弟类进行改装或加装功能时,可以使用装饰器模式。例如,在一个图形界面工具箱中为多个不同的组件提供一些公共的功能时,可以使用装饰器模式来实现。

java 代码示例

以下是一个实现装饰器模式的 java 示例代码

  1. 定义了一个抽象组件接口 Shape 和两个具体组件类 Circle 和 Rectangle,

//抽象组件接口
public interface Shape {
    void draw();
}

//具体组件类:圆形
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

//具体组件类:矩形
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}
  1. 定义一个抽象装饰器类 ShapeDecorator 和两个具体装饰器类 RedShapeDecorator 和 GreenShapeDecorator,

//抽象装饰器类
public abstract class ShapeDecorator implements Shape {
    //持有一个抽象组件对象
    protected Shape shape;

    //构造方法
    public ShapeDecorator(Shape shape) {
        this.shape = shape;
    }

    //调用被包装对象的方法
    @Override
    public void draw() {
        shape.draw();
    }
}

//具体装饰器类:红色装饰器
public class RedShapeDecorator extends ShapeDecorator {
    //构造方法
    public RedShapeDecorator(Shape shape) {
        super(shape);
    }

    //重写draw方法,在调用被包装对象的方法之前或之后添加新的功能
    @Override
    public void draw() {
        //调用被包装对象的方法
        super.draw();
        //添加新的功能
        setRedBorder();
    }

    //定义新的功能方法
    private void setRedBorder() {
        System.out.println("Setting red border");
    }
}

//具体装饰器类:绿色装饰器
public class GreenShapeDecorator extends ShapeDecorator {
    //构造方法
    public GreenShapeDecorator(Shape shape) {
        super(shape);
    }

    //重写draw方法,在调用被包装对象的方法之前或之后添加新的功能
    @Override
    public void draw() {
        //调用被包装对象的方法
        super.draw();
        //添加新的功能
        setGreenBorder();
    }

    //定义新的功能方法
    private void setGreenBorder() {
        System.out.println("Setting green border");
    }
}

  1. 编写装饰器模式测试代码,main 函数中创建了不同的组件和装饰器对象,并调用了它们的方法,

//测试类
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        //创建一个圆形对象
        Shape circle = new Circle();
        //创建一个矩形对象
        Shape rectangle = new Rectangle();
        //创建一个红色装饰器对象,包装圆形对象
        Shape redCircle = new RedShapeDecorator(circle);
        //创建一个绿色装饰器对象,包装矩形对象
        Shape greenRectangle = new GreenShapeDecorator(rectangle);

        //调用各个对象的方法,展示不同的效果
        System.out.println("Normal circle:");
        circle.draw();
        System.out.println("Normal rectangle:");
        rectangle.draw();
        System.out.println("Red circle:");
        redCircle.draw();
        System.out.println("Green rectangle:");
        greenRectangle.draw();
    }
}

输出结果如下:

Normal circle:
Drawing a circle
Normal rectangle:
Drawing a rectangle
Red circle:
Drawing a circle
Setting red border
Green rectangle:
Drawing a rectangle
Setting green border

Spring 代码示例

要想再 Spring 项目中应用装饰器模式,只需对以上代码进行简单改造即可,

  1. 给具体组件类 Circle、Rectangle 添加 @Component 注解,

@Component
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

@Component
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}
  1. 给具体装饰器类 RedShapeDecorator 和 GreenShapeDecorator 类添加 @Component 注解,

@Component
public class GreenShapeDecorator extends ShapeDecorator {
    // 构造方法
    public GreenShapeDecorator(@Qualifier("rectangle") Shape shape) {
        super(shape);
    }

    // 重写draw方法,在调用被包装对象的方法之前或之后添加新的功能
    @Override
    public void draw() {
        // 调用被包装对象的方法
        super.draw();
        // 添加新的功能
        setGreenBorder();
    }

    // 定义新的功能方法
    private void setGreenBorder() {
        System.out.println("Setting green border");
    }
}

@Component
public class RedShapeDecorator extends ShapeDecorator {
    // 构造方法
    public RedShapeDecorator(@Qualifier("circle") Shape shape) {
        super(shape);
    }

    // 重写draw方法,在调用被包装对象的方法之前或之后添加新的功能
    @Override
    public void draw() {
        // 调用被包装对象的方法
        super.draw();
        // 添加新的功能
        setRedBorder();
    }

    // 定义新的功能方法
    private void setRedBorder() {
        System.out.println("Setting red border");
    }
}
  1. 编写 Spring 项目测试代码,

@SpringBootTest
@RunWith(SpringRunner.class)
public class DecoratorTest {

    // 从Spring容器中获取Context对象
    @Autowired
    private RedShapeDecorator redCircle;
    @Autowired
    private GreenShapeDecorator greenRectangle;

    @Test
    public void test() {
        System.out.println("Red circle:");
        redCircle.draw();
        System.out.println("Green rectangle:");
        greenRectangle.draw();
    }
}

输出结果如下:

Red circle:
Drawing a circle
Setting red border
Green rectangle:
Drawing a rectangle
Setting green border

总结

装饰器模式可以将不同功能的单个模块规划至不同的装饰器类中,各装饰器类独立自主,各司其职。客户端可以根据自己的需求自由搭配各种装饰器,每加一层装饰就会有新的特性体现出来,巧妙的设计让功能模块层层叠加,装饰之上套装饰,最终使原始对象的特性动态地得到增强。

关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/980128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Xilinx ZYNQ 7000学习笔记三(小结)

1 启动模式: ZYNQ 7000的启动模式由外部引脚决定的,5个模式引脚MIO[6:2]用于配置NAND flash、并行NOR flash、Serial NOR (Quad-SPI)、SD flash以及JTAG 一共5种启动模式。具体而言就是复位时,zynq-7000 SOC对下述引脚进行连续3个时钟周期采…

从Overleaf提交到arxiv

1.文件结构注意避坑!!! 前期在overleaf写论文时,为了使文件结构目录清晰,分别建了section, image等子文件夹,即 >project>image-XXx.pdf-xxx.pdf>sections-abstract.tex-method.tex>main.tex…

ProcessWindowFunction 结合自定义触发器的陷阱

背景: flink中常见的需求如下:统计某个页面一天内的点击率,每10秒输出一次,我们如果采用ProcessWindowFunction 结合自定义触发器如何实现呢?如果这样实现问题是什么呢? ProcessWindowFunction 结合自定义触发器实现…

基于YOLOv8模型的安全背心目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要:基于YOLOv8模型的安全背心目标检测系统可用于日常生活中检测与定位安全背心目标,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训…

Zabbix 利用 Grafana 进行图形展示

安装插件 配置数据源 导入模版 查看 1.安装 wget https://mirrors.tuna.tsinghua.edu.cn/grafana/yum/rpm/Packages/grafana-10.0.0-1.x86_64.rpm [rootrocky8 apps]# yum install grafana-10.0.0-1.x86_64.rpm [rootrocky8 apps]# systemctl start grafana-server.service …

Mac 手动安装 sshpass

1. 下载安装包 https://sourceforge.net/projects/sshpass/ 解压并进入到安装包目录 tar -zxvf sshpass-xx.xx.tar.gz cd sshpass-xx.xx2. 检验环境,编译源码安装 ./configuremake&&make install3. 检测安装是否成功 ▶ sshpass Usage: sshpass [-f|-…

freertos之资源管理

中断屏蔽 屏蔽中断函数 在任务中使用 taskENTER_CRITICA()/taskEXIT_CRITICAL() 在中断中使用 taskENTER_CRITICAL_FROM_ISR()/taskEXIT_CRITICAL_FROM_ISR() 功能介绍 使用上述函数,进入临界中断,任务不会切换,且中断优先级处于con…

邮件钓鱼的防守策略

一、攻击背景 在历年的实战攻防演练中,人的漏洞是网络安全最大的脆弱点,而钓鱼攻击就是从内部攻破堡垒至关重要的手段。攻击者通过伪装成可信来源发送虚假邮件,诱导接收者点击恶意链接、提供敏感信息或执行恶意附件,从而获取机密…

python解压gz包

import gzipdef un_gz(file_name):# 获取文件的名称,去掉后缀名f_name file_name.replace(".gz", "")# 开始解压g_file gzip.GzipFile(file_name)#读取解压后的文件,并写入去掉后缀名的同名文件(即得到解压后的文件&am…

聊聊操作系统中 进程 and 线程中哪些事??

操作系统(英语:Operating System,缩写:OS)是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境,操作系统可以分为桌面操作系统&a…

java 身份证号码验证

需要编号文件 编号文件部分内容如下 11:北京市 1101:市辖区 110101:东城区 110102:西城区 110105:朝阳区 110106:丰台区 110107:石景山区 110108:海淀区 ...... 编号文件内容比较多 csdn点击 下载地址 java代码如下 import java.io.*; import java.text.ParseException; im…

Win10怎么设置默认看图软件?Win10设置自带看图软件为默认程序操作方法

大小:4.19 GB类别:雨林木风系统 Win10怎么设置默认看图软件?有用户在使用电脑去浏览图片内容的时候,总是使用第三方的看图软件自动打开,不使用系统自带的看图软件来开启图片。那么怎么设置系统自带的看图软件为默认程序…

【嵌入式软件C编程】主函数free子函数malloc地址的两种方式以及注意事项

本文档主要记录嵌入式C语言在子函数中应用malloc函数的方式,在实际项目中内存管理特别重要 一般在主函数中(main)使用malloc函数,然后在通过free函数进行释放内存,但有时候如果必须在子函数长调用malloc函数该怎样进行…

[HDCTF 2023]YamiYami

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言涉及知识点解题详细过程session伪造反弹shell 前言 从暑假末尾一直搁置,当时卡在反弹shell搞得离flag就差一步。不过最近一两天学习完反弹shell的知…

面试算法-数据结构二

大厂算法面试 1) 图论 2) 大数据 3)动态规划 优秀的算法往往取决于你采取那种数据结构 高级数据结构 1)优先队列 2)图 3)前缀树 4)线段树 5)树状数组 在分析问题的时候&a…

OB Cloud 初体验

文章来源:韩锋频道 韩锋 数据库行业资深从业者,著有《SQL 优化最佳实践》、《数据库高效优化》等数据库相关著作。 OceanBase(下文简称OB) 作为国内一款优秀的分布式数据库,这些年来发展很快,在金融、电商…

UDP协议结构及其注意事项

UDP报文结构 UDP报文结构主要是由两个部分组成的:UDP头部和数据部分。 UDP头部 源端口号:16位字段,指示数据发送方的端口号。目的端口号:16位字段,指示数据接收方端口号。UDP报文长度:16位字段&#xff0…

new HashMap{{put(“a“,“b“)}}

如题,这是什么鬼? Runnable r new Runnable(){Overridepublic void run() {}}; 上面这种写法大家不陌生吧,实际上 就是 定义了一个匿名内部类。 比如:下面这个就是 定义了一个 匿名的Test子类,旁边那个向下的箭头就…

Lua02——应用场景及环境安装

应用场景 是当今游戏领域使用最广泛的脚本语言之一。 搭配 OpenResty 使用,可以扩展Nginx服务器的功能,使用者仅需要编写Lua代码就能轻松完成业务逻辑。 与 Redis 结合。 Adobe Photoshop Lightroom 搭配 Lua 编写插件。 与游戏结合: C/…

DNS域名解析 不同网段的 实验

路漫漫其修远兮,吾将上下而求索 前言 知识应该是 有难宜简,用简单的语言讲清楚,而不是干拧螺丝的事,却说着造火箭的理论。 (尤其是大学讲台上的那一部分人,想想人家的学费,你的工资&#xff…