【设计模式】结构型-装饰器模式

news2025/1/19 14:20:24

在代码的海洋深处迷离,藏匿着一片神奇之地。那里有细腻的线条交错,是装饰器的奇妙艺术。

文章目录

  • 一、登录的困境
  • 二、装饰器模式
  • 三、装饰器模式的核心组成部分
  • 四、运用装饰器模式
  • 五、装饰器模式的应用场景
  • 六、小结
  • 推荐阅读

一、登录的困境

假设我们有一个 User 类,它有一些基本的功能,比如 login()logout()。现在,如果我们想要添加一些额外的功能,比如日志记录、权限检查等,我们可能会这样做:

public class User {
    public void login() {
        System.out.println("Logging in...");
    }

    public void logout() {
        System.out.println("Logging out...");
    }

    public void logActivity() {
        System.out.println("Logging activity...");
    }

    public void checkPermission() {
        System.out.println("Checking permission...");
    }
}

然后,我们可以像这样使用它:

public class Main {
    public static void main(String[] args) {
        User user = new User();
        user.login();
        user.logActivity();
        user.checkPermission();
        user.logout();
    }
}

这种方法的问题在于,我们的 User 类变得越来越复杂,每次添加新的功能,我们都需要修改 User 类。这使得代码难以维护和扩展。

二、装饰器模式

装饰器模式是一种结构型设计模式,它允许动态地将新功能附加到对象上。这种模式通常用于遵循单一职责原则,即每个类应该只负责一种类型的功能。装饰器模式通过将对象封装在一个装饰器类的实例中,然后再封装到另一个装饰器类的实例中,以此类推,来实现功能的添加或修改,而不需要修改原始对象的代码。

装饰器模式的特点:

  1. 动态添加功能: 装饰器模式允许在运行时动态地给对象添加新的功能,而不需要修改原始对象的代码。通过装饰器,可以在不改变现有对象结构的情况下,扩展其功能。
  2. 保持单一职责原则: 装饰器模式遵循了设计原则中的单一职责原则。每个装饰器类都只关注于添加一个特定的功能,而不会包含其它的无关逻辑,从而保持了类的简洁性和可维护性。
  3. 避免类爆炸: 装饰器模式避免了通过继承来扩展功能可能导致的类爆炸问题。通过组合和嵌套装饰器,可以灵活地组合不同的功能,而不需要创建大量的子类。
  4. 与继承相比更灵活: 装饰器模式提供了一种比继承更加灵活的方式来扩展对象的功能。通过组合和嵌套装饰器,可以实现更复杂的功能组合,而不会产生类层次结构的复杂性。
  5. 可透明地使用原始对象: 使用装饰器模式时,客户端可以透明地使用原始对象,不需要知道装饰器的存在。装饰器和原始对象拥有相同的接口,因此可以完全替代原始对象的使用。

三、装饰器模式的核心组成部分

装饰器模式的核心组成包括以下几个要素:

  1. Component(组件): 这是一个接口或者抽象类,定义了被装饰对象和装饰器对象的公共接口,确保它们可以相互替换。
  2. ConcreteComponent(具体组件): 这是实现了 Component 接口的具体类,是被装饰的对象。它定义了最基本的行为,可以被一个或多个装饰器扩展。
  3. Decorator(装饰器): 这是一个抽象类或者接口,与 Component 具有相同的接口,并且包含一个指向 Component 对象的引用。它通常是一个抽象类,提供了一个默认实现来调用被装饰对象的原始方法,或者添加一些共享的功能。
  4. ConcreteDecorator(具体装饰器): 这是实现了 Decorator 接口的具体类,用于给 ConcreteComponent 添加额外的功能。它可以有一个或多个具体装饰器,可以嵌套使用,每个装饰器都对组件的行为进行了修改或者扩展。

在这里插入图片描述

这个类图展示了 Component、ConcreteComponent、Decorator 和 ConcreteDecorator 之间的关系。Component 是被装饰对象和装饰器对象的公共接口,ConcreteComponent 是具体的被装饰对象,Decorator 是装饰器的抽象类,ConcreteDecorator 是具体的装饰器类。

四、运用装饰器模式

场景假设:我们需要在之前已经拥有的登录功能上多增加一项功能:在用户登录时显示一条欢迎消息。我们可以使用装饰器模式来实现这个功能。

  1. 定义组件接口: 首先,定义一个接口或抽象类,代表被装饰的对象。这个接口应该声明装饰器和被装饰者共同拥有的方法。

    // UserService.java
    public interface UserService {
        void login(String username);
    }
    
  2. 创建具体组件类: 实现组件接口,创建一个具体的类,代表基本的被装饰对象。这个类提供了最基本的功能。

    // SimpleUserService.java
    public class SimpleUserService implements UserService {
        @Override
        public void login(String username) {
            System.out.println("User " + username + " logged in.");
        }
    }
    
  3. 定义装饰器抽象类或接口: 创建一个抽象类或接口,用于定义装饰器的公共接口。这个类通常会包含一个指向组件接口的引用,以便装饰器可以对被装饰者进行操作。

    // UserServiceDecorator.java
    public abstract class UserServiceDecorator implements UserService {
        protected UserService decoratedUserService;
    
        public UserServiceDecorator(UserService decoratedUserService) {
            this.decoratedUserService = decoratedUserService;
        }
    
        @Override
        public void login(String username) {
            decoratedUserService.login(username);
        }
    }
    
  4. 创建具体装饰器类: 创建一个或多个具体的装饰器类,它们实现了装饰器接口,并包含一个指向组件接口的引用。这些装饰器类可以添加额外的功能或修改组件的行为。

    // WelcomeMessageDecorator.java
    public class WelcomeMessageDecorator extends UserServiceDecorator {
        public WelcomeMessageDecorator(UserService decoratedUserService) {
            super(decoratedUserService);
        }
    
        @Override
        public void login(String username) {
            super.login(username);
            // 新增的欢迎功能
            System.out.println("Welcome, " + username + "!");
        }
    }
    
  5. 使用装饰器: 在客户端代码中,创建一个具体的组件对象,并用装饰器对象对其进行包装。可以通过链式调用多个装饰器来实现复杂的装饰效果。

    // Main.java
    public class Main {
        public static void main(String[] args) {
            // 创建一个简单的用户服务
            UserService simpleUserService = new SimpleUserService();
    
            // 使用装饰器添加欢迎消息
            UserService userServiceWithWelcomeMessage = new WelcomeMessageDecorator(simpleUserService);
    
            // 模拟用户登录
            userServiceWithWelcomeMessage.login("Alice");
        }
    }
    

五、装饰器模式的应用场景

装饰器模式在许多场景中都非常有用,特别是当需要动态地、透明地(不影响其他对象)给对象添加职责时。以下是一些常见的应用场景:

  1. 图形用户界面工具箱:在图形用户界面工具箱中,装饰器模式可以用来动态地添加新的功能。例如,你可以创建一个基本的窗口对象,然后使用装饰器来添加滚动条、边框、标题等。
  2. Java I/O:Java 的 I/O 类库就广泛使用了装饰器模式。例如,你可以创建一个基本的 FileInputStream 对象,然后使用装饰器如 BufferedInputStream 来增加缓冲功能。
  3. Web 开发中的中间件:在 Web 开发中,装饰器模式常常被用作中间件,允许在处理请求和响应之前或之后插入额外的逻辑,比如日志记录、错误处理、身份验证等。
  4. 测试和校验:在写测试或者进行数据校验时,装饰器模式也很有用。可以使用装饰器来添加额外的校验逻辑,或者在测试中模拟特定的条件。

这些只是装饰器模式的一些应用场景,实际上,只要你需要动态地给对象添加职责,而又不想增加子类的复杂性,装饰器模式就可能会是一个好的选择。

六、小结

装饰器模式是一种非常灵活的设计模式,它可以帮助我们在运行时动态地扩展对象的功能,同时保持对象接口的一致性。通过合理地运用装饰器模式,我们可以使代码更加灵活、可维护,并且更容易扩展。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

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

相关文章

【经验分享】搭建跨境电商那个独立站必备的功能模块以及实现

搭建跨境电商独立站时,需要确保网站具备一系列关键的功能板块,以提供用户友好的购物体验并确保业务的顺利进行。以下是这些功能板块的详细归纳: 注册登录与身份验证: 用户注册与登录:允许用户创建账户,通过…

CST纳米光学 --- LSPR局部等离子激元共振,消光截面ECS,法诺共振

这期我们用自带的Drude散射粒子,计算消光截面。 查看模型,内核是Silica二氧化硅,正常的介质材料,半径是38纳米: 外围是Drude模型的金属材料包裹,半径48纳米,该材料的参数可由宏Materials->Cr…

洁净室气流流型分类及气流流型可视化验证

洁净室气流 流型的分类 洁净室是空气悬浮粒子浓度受控的房间,其建造和使用方式可最大限度减少房间进入的、产生的和滞留的粒子。房间内的温度、湿度、压力等其他相关参数均按要求受控(ISO14644-6)。 #深度好文计划# 一.洁净室的四大技术要素…

招募来袭 | 与热爱技术的谷歌开发者一起创造精彩

写在前面 技术的进步在不断推动着世界发展。从 Android、Flutter 等产品的稳步更新迭代,到秉承着负责任的态度对 AI 进行探索,我们通过每一次的技术跃进,帮助大家打开新的视野,激发更多的灵感,将我们的工具和平台打造成…

线控转向 0 -- 线控转向介绍和专栏规划

一、线控转向介绍 高阶自动驾驶核心部件:英创汇智线控转向解决方案 _北京英创汇智科技有限公司 (trinova-tech.com) 线控转向的系统组成详细介绍大家可以看上面这个链接;我这里也只从里面截取一些图片,简单说明。 1、结构组成 线控转向分为…

【前端基础】CSS介绍|CSS选择器|常用CSS

目录 一、CSS介绍 1.1 什么是CSS 1.2 基本语法规范 1.3 引⼊⽅式 1.4 规范 💡二、CSS选择器 1. 标签选择器 2. class选择器 3. id选择器 4. 复合选择器 5. 通配符选择器 三、常用CSS 3.1 color 3.2 font-size 3.3 border 3.4 width/height 3.5 padd…

C51单片机 串口打印printf重定向

uart.c文件 #include "uart.h"void UartInit(void) //4800bps11.0592MHz {PCON | 0x80; //使能波特率倍速位SMODSCON 0x50; //8位数据,可变波特率。使能接收TMOD & 0x0F; //清除定时器1模式位TMOD | 0x20; //设定定时器1为8位自动重装方式TL1 0xF4; //设…

BUG解决: Zotero 文献GBT7714无法正常调用

1. 下载csl文件 网上有推荐直接下载现成版本的,比如参考资料【1】的蓝奏云文件,但是还是无法实现功能(空文档中可以用了)。 2. Github版本 也有说网盘版本和那个 Juris-M 的 CSL bug 太多的。 总结 后面发现,只需…

【生产排查】解决 Kettle 没有运行日志的问题

文章目录 背景排查解决第 1 步:修改 kettle-service.conf,添加日志配置第 2 步:使用最新配置文件重启 kettle第 3 步:验证日志配置是否生效背景 🚀 项目使用 Kettle 作为 ETL 工具,从外部系统中抽取数据、转换数据、并最终加载到本项目的数据库中。 😂 存在的问题:据…

JVM学习-详解类加载器(二)

双亲委派机制 双亲委派优势 避免类的重复加载,确保一个类的全局唯一性 Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层次关系可以避免类的重复加载,当父类已经加载了该类,就没有必要子ClassLoader再加载…

ai辅助教育孩子

孩子经常躺在床上看手机是一个需要关注的问题,因为这不仅可能影响他们的视力健康,还可能影响睡眠质量和身体健康。以下是一些建议,帮助应对这一问题: 设定明确的规定: 与孩子一起制定使用手机的规则,如每天…

nginx c++模块编译

不论是c还是c,nginx的第三方模块编写没什么太区别,但是提供给nginx调用的,必须是纯c的接口。 先说下为什么不能使用c编译nginx,nginx是纯c写的,而且c是兼容c的,但是用c(g)编译nginx的框架,就会出…

呆滞物料规范管理了,问题就好办了

对于制造企业来说,库存是生存和发展的重要保障,过高的库存会占用企业大量的资金和管理成本,影响企业的正常生产,然而多数中小制造企业还在用人工干预管理,如何控制呆滞物料成为仓储管理的一大难题。 什么是呆滞料&…

【JAVASE】日期与时间类(下)

三:LocalDateTime 相当于LocalDate类,在LocalDateTime类的对象中还可以封装时、分、秒和纳秒(1纳秒是1秒的十亿分之一)等时间类型。 例如,创建LocalDateTime对象 , LocalDateTime date LocalDateTi…

【Docker】上海交通大学开源镜像站服务变更:Docker 用户需迅速行动

近日,上海交通大学开源镜像站宣布了一个重大变更,对国内Docker用户来说,这一消息无疑具有紧迫性。 镜像站服务的变更 上海交通大学开源镜像站一直是国内Docker用户的重要资源,它提供了快速下载DockerHub仓库镜像的服务。然而&a…

LiDAR360MLS 7.2.0 雷达点云数据处理软件功能介绍

新增模块和功能: 支持手持、背包数据的解算 SLAM解算成功率提升 SLAM解算效率提升 采集端与后处理端保持一致 赋色优化 新增平面图模块 新增平面图全自动矢量化功能 新增平面图矢量一键导出DXF功能 新增平面图正射影像一键导出功能 支持交叉、垂直绘制 支…

如何安装 CleanMyMac X 4.15.3破解版

CleanMyMac X 4.15.3破解版是一款专业的Mac系统清理软件,可一键智能扫描清理mac系统日志缓存磁盘垃圾和多余语言安装包,快速释放电脑内存,轻松管理和升级Mac上的应用。同时CleanMyMac X 破解版可以强力卸载恶意软件,修复系统漏洞&…

数据挖掘--聚类分析:基本概念和方法

数据挖掘--引论 数据挖掘--认识数据 数据挖掘--数据预处理 数据挖掘--数据仓库与联机分析处理 数据挖掘--挖掘频繁模式、关联和相关性:基本概念和方法 数据挖掘--分类 数据挖掘--聚类分析:基本概念和方法 聚类分析 聚类分析是把一个数据对象&…

Java基础——数组Array

系列文章目录 文章目录 系列文章目录前言一、数组基本概念二、一维数组三、数组的模型四、数组对象的创建五、元素为引用数据类型的数组 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网…