对装饰器模式的理解

news2025/2/25 21:26:25

目录

  • 一、场景
  • 二、面对场景中的新需求,我们怎么办?
    • 1、暴力法:直接修改原有的代码。
    • 2、子类继承法:既然要增强行为,那我搞一个子类,覆写不就完事了?
    • 3、装饰器模式
  • 三、对装饰器模式的思考
    • 1、从代码结构上来看,咋和代理模式这么像呢?
    • 2、设计原则 >> 设计模式

一、场景

  • 在Java中,提供某种功能/服务给客户端(应用层的类)去使用,一般都是先定义接口,然后实现该接口。
// 应用层
@Component
public class DataSourceApplication {
    @Autowired
    private DataSource dataSource;

    public void write(String data) {
        dataSource.writeData(data);
    }

    public String read() {
        return dataSource.readData();
    }
}
// 服务层
public interface DataSource {
    void writeData(String data);
    String readData();
}

@Service
public class FileDataSourceImpl implements DataSource {
    @Override
    public void writeData(String data) {
        System.out.println("[FileDataSourceImpl.writeData]");
    }

    @Override
    public String readData() {
        System.out.println("[FileDataSourceImpl.readData]");
        return "FileDataSourceImpl.readData";
    }
}
  • 这时候,产品经理要求写入数据前要先加密,读出数据后要解密。

二、面对场景中的新需求,我们怎么办?

1、暴力法:直接修改原有的代码。

@Service
public class FileDataSourceImpl implements DataSource {
    @Override
    public void writeData(String data) {
    	// 对数据加密
    	...
        System.out.println("[FileDataSourceImpl.writeData]");
    }

    @Override
    public String readData() {
        System.out.println("[FileDataSourceImpl.readData]");
        // 对数据进行解密
        ...
        return "FileDataSourceImpl.readData";
    }
}
  • 虽然这种做法存在一些问题,但在公司可能很普遍…(不然屎山怎么形成的?😃)
    • 问题1:之前辛苦写的单测白费了。(虽然并不是所有程序员都会写单测 😃)
    • 问题2:逻辑耦合,数据的读写和数据的加密,从客观上来说,是相互独立的逻辑。
      • 如果我们耦合在一起,那么方法会越来越冗长,逻辑也越来越不清晰。例如,产品经理又改需求了,要求支持多种加密算法。

2、子类继承法:既然要增强行为,那我搞一个子类,覆写不就完事了?

@Primary
@Service
public class EncryptFileDataSourceImpl extends FileDataSourceImpl {
    @Override
    public void writeData(String data) {
        // 对数据进行加密
        String encryptedData = null;
        ...
        super.writeData(encryptedData);
        
        System.out.println("[EncryptFileDataSourceImpl.writeData]");
    }

    @Override
    public String readData() {
		super.readData();
		
        // 对数据进行解密
        ...
        
        System.out.println("[EncryptFileDataSourceImpl.readData]");
        return "EncryptFileDataSourceImpl.readData";
    }
}
  • 如果产品经理要求先压缩数据,再加密,最后写入数据呢?难道咱再搞一个子类?
    • 咱最好避免这种链式的继承。

3、装饰器模式

  • 实现:
    • (1)压缩数据 -> 加密数据 -> 写入数据
    • (2)读取数据 -> 解密数据 -> 解压数据
  • 代码:
public interface DataSource {
    void writeData(String data);
    String readData();
}

public class FileDataSourceImpl implements DataSource {
    @Override
    public void writeData(String data) {
        System.out.println("[FileDataSourceImpl.writeData] 写入数据");
    }

    @Override
    public String readData() {
        return "[FileDataSourceImpl.readData] 读取数据";
    }
}

public class EncryptDataSourceImpl implements DataSource {
    private DataSource dataSource;

    public EncryptDataSourceImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void writeData(String data) {
        System.out.println("[EncryptFileDataSourceImpl.writeData] 加密数据");
        dataSource.writeData(data);
    }

    @Override
    public String readData() {
        System.out.println(dataSource.readData());
        return "[EncryptFileDataSourceImpl.readData] 解密数据";
    }
}

public class CompressDataSourceImpl implements DataSource {
    private DataSource dataSource;

    public CompressDataSourceImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void writeData(String data) {
        System.out.println("[CompressDataSourceImpl.writeData] 压缩数据");
        dataSource.writeData(data);
    }

    @Override
    public String readData() {
        System.out.println(dataSource.readData());
        return "[CompressDataSourceImpl.readData] 解压数据";
    }
}
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource compressDataSourceImpl() {
        return new CompressDataSourceImpl(new EncryptDataSourceImpl(new FileDataSourceImpl()));
    }
}
@ComponentScan
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        dataSource.writeData("hello world");
        System.out.println("-----------------------------");
        System.out.println(dataSource.readData());
    }
}

/*
[CompressDataSourceImpl.writeData] 压缩数据
[EncryptFileDataSourceImpl.writeData] 加密数据
[FileDataSourceImpl.writeData] 写入数据
-----------------------------
[FileDataSourceImpl.readData] 读取数据
[EncryptFileDataSourceImpl.readData] 解密数据
[CompressDataSourceImpl.readData] 解压数据
*/
  • 一旦产品经理要改需求了,例如:
    • (1)压缩数据 -> 写入数据
    • (2)读取数据 -> 解压数据
  • 咱只要修改DataSourceConfig即可:
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource compressDataSourceImpl() {
//        return new CompressDataSourceImpl(new EncryptDataSourceImpl(new FileDataSourceImpl()));
        return new CompressDataSourceImpl(new FileDataSourceImpl());
    }
}

/*
[CompressDataSourceImpl.writeData] 压缩数据
[FileDataSourceImpl.writeData] 写入数据
-----------------------------
[FileDataSourceImpl.readData] 读取数据
[CompressDataSourceImpl.readData] 解压数据
*/

三、对装饰器模式的思考

1、从代码结构上来看,咋和代理模式这么像呢?

  • 装饰器模式:
    • (1)实现和被装饰类一样的接口(如上述的:implements DataSource
    • (2)持有被装饰的类(如上述的:private DataSource dataSource;
    • (3)增强接口的方法
  • 代理模式:【详见:对代理模式的理解】
    • (1)实现和被代理类一样的接口
    • (2)持有被代理的类
    • (3)增强接口的方法

像,太像了!

  • 真的是这样吗?
    • 看看客户端是如何使用的吧~
      • 装饰器模式:我们要根据需求,“装饰”出接口对应的实现类。
        • 通过层层套娃,丰富基础功能。结合上文,从基本的写入数据功能,丰富为:压缩数据 -> 加密数据 -> 写入数据。
      • 代理模式:接口原本的实现类是类A,代理后,客户端真正使用的是实现类B(通常,客户端感知不到这种变化)。
  • 一图胜前言:
    在这里插入图片描述
    • 一个接口的实现类,从逻辑上说,存在组合逻辑,例如:
      • 加密数据 + 写入数据
      • 压缩数据 + 写入数据
      • 压缩数据 + 加密数据 + 写入数据
    • 如果采用继承的方式,会导致定义很多子类,那么用组合吧!用装饰器模式吧!【Java的io便是这种场景~】

2、设计原则 >> 设计模式

  • 我也学了一阵子设计模式了,已经感受到设计模式的局限性了。以上文为例,如果我们拿到的FileDataSourceImpl已经是一坨屎山了,里面写入数据的逻辑并不纯粹,那么,通过装饰器模式丰富写入数据的能力可能会出问题(例如,写入数据前做了一些特殊处理;需求是我们先做特殊处理,再加密后写入;使用装饰器模式就变成了,先加密,然后特殊处理,再写入。)。此时,还不如暴力法来得简洁高效。

破罐子破摔,世界是熵增的…

  • 因此,设计模式非常依赖场景。场景稍微变一下,设计模式就失效了… 而真正有用的是,设计模式遵循的设计原则,以及背后的终极奥义:高内聚,低耦合

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

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

相关文章

今天刷两题(day2)

题目一:最长公共前缀 题目描述: 给你一个大小为 n的字符串数组 strs ,其中包含n个字符串 , 编写一个函数来查找字符串数组中的最长公共前缀,返回这个公共前缀。输入输出描述: 输入:"abca","…

Python这十大特征,堪称“圆满”!

当你犹豫是否要开始 Python 学习之前,可以先详尽了解一下这门编程语言。 软件开发者 Guido Van Rossum 于 1991 年创建了 Python,旨在使程序员的工作更加简单。Python 是目前全球比较流行且产业急需的程序设计语言,也是一门跨平台、开源、免…

AI大模型探索之路-应用篇11:AI大模型应用智能评估(Ragas)

目录 前言 一、为什么要做智能评估? 二、Ragas是什么? 三、Ragas使用场景 四、Ragas评估指标 五、Ragas代码实践 总结 前言 随着人工智能技术的飞速发展,AI大模型(LLM)已经成为了推动技术创新和应用的关键因素。…

【个人博客搭建】(6)搭建swagger文档

1、前言: 其实在创建webapi时我们就已经选择了swagger了。 也就是代码 app.UseSwagger(); app.UseSwaggerUI(); 但是我们可以对其进行拓展,补充信息。 像常需要的action接口中文信息,Controller控制器中文信息等... 进入正题。 2、添加…

009、Python+fastapi,第一个后台管理项目走向第9步:ubutun 20.04下安装vscode+git环境配置

一、说明 git是一定要配置的,由于是白嫖的无影云电脑,东西得保存在网上,就继续白嫖gitee吧,显然国内github是不太合适的了 二、安装git 直接安装sudo apt install -y git git --version git version 2.25.1 三、配置git 在git上…

走近网络安全公司:F5探索应用安全至简之道

伴随企业加速数字化转型工作、扩展到新的基础设施环境并采用微服务架构,企业正在拥抱混合和多云基础设施所带来的灵活性。现在跨越四种环境部署应用的企业,其平均需要管理和保护的应用数据路径比应用仅限本地部署时多10倍,复杂性呈指数级增加…

Matlab r2023b Simulink 给子系统添加封面

写这篇记录的原因是,r2023b版本里改动了自定义封面的界面,而我是一个新手小白,零基础,探索一天之后发现实现方法。最终效果如图: 步骤1:打开软件,点击Simulink,再打开含有子系统的工…

【贪心 堆 】3081. 替换字符串中的问号使分数最小

算法可以发掘本质,如: 一,若干师傅和徒弟互有好感,有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。 二,有无限多1X2和2X1的骨牌,某个棋盘若干格子坏了,如何在没有坏…

一夜爆红的4款国产软件,却一度被大众误以为是外国人开发

在现今高度信息化的时代,计算机已经深深地渗透到了我们生活的每一个角落。 从日常的办公学习到娱乐休闲,几乎都离不开计算机技术的支持。而在这背后,软件作为计算机的灵魂,其发展历史可谓波澜壮阔。 中国软件产业经过多年的积累和…

基于Copula函数的风光功率联合场景生成_任意修改生成的场景数目(附带Matlab代码)

基于Copula函数的风光功率联合场景生成 削减为6个场景 部分展示削减为5个场景 部分展示 风光等可再生能源出力的不确定性和相关性给系统的设计带来了极大的复杂性,若忽略这些因素,势必会在系统规划阶段引入次优决策风险。因此,在确定系统最佳…

JavaScript权威指南(第7版) 笔记 - 第 7 章 数组

能用代码说清楚的,绝不多废话!!!!!! Linux创始人Linus的名言:Talk is cheap,show me the code ! ,博主技术博文会精心给出能说明问题的范例代码!…

【Linux】Linux信号

目录 信号的概念 生活中的信号 Linux中的信号 kill命令 kill 命令的使用 常见的信号 命令行代码示例 注意事项 信号的处理方式 产生信号 信号的捕捉 信号捕捉示意图 内核如何实现信号捕捉 信号的捕捉与处理 小结 阻塞信号 信号在内核中的表示图 信号集操作函数…

基于大数据的全国热门景点数据可视化分析系统

温馨提示:文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 本文将介绍如何使用Python中的Pandas库进行数据挖掘,并结合Flask Web框架实现一个旅游景点数据分析系统。该系统将包括以下功能模块:热门景点概况、景点星级与评分分析、景…

AI光芯登上Science,开启算力新纪元

智能光芯片“太极”:清华大学的科技壮举,开启算力新纪元 在科技的浩瀚星海中,每一次创新都是对未知世界的探索和征服。近日,清华大学电子工程系与自动化系的联合团队,凭借其深厚的科研实力和创新精神,研发出…

[openGL] 高级光照-Gamma矫正

目录 一 Gamma是什么? 二 感知光度和物理光度 2.1 与Gamma的关系 2.3 存在问题和弊端? 三 Gamma矫正(逆Gamma) 3.1 Gamma矫正的两种方法 3.2 sRGB空间 3.3 重复校正 3.3.1 在着色器中处理重复校正 3.3.2 在加载纹理时就重复校正 3.3.3 校正前后效果 本章节Qt源码点…

基于python的二手房数据分析建模及可视化研究,爬取链家二手房数据,可视化分析,房价预测模型

介绍 主要涉及通过爬取济南市链家二手房数据,然后对数据进行处理,包括缺省值处理,高德地图获取二手房地址所属市区,经纬度等数据处理。然后通过python的flask框架编写后端接口,把数据响应给前端。然后前端通过AJAX请求…

AI决策与专家决策,您更喜欢哪种决策方式?

HI,我是AI智能小助手CoCo。 CoCode开发云智能助手CoCo “大家好,我是CoCode开发云的AI智能小助手CoCo,现在为大家播放关于CoCode开发云AI大家庭的最新消息: 欢迎AI家庭新成员:AI自动决策”。 AI自动决策发布 CoCode开…

信息系统安全,陈萍,王金双习题

第一章 填空题 机密性、完整性、可用性设备安全、数据安全、内容安全、行为安全通信保密、信息安全、信息安全保障保护、检测、响应、恢复健康、法律法规网络和通信协议的脆弱性、信息系统的缺陷、黑客的恶意攻击稳定性、可靠性、可用性硬件和软件的底层整体性、分层性、最小…

Zabbix监控Windows

1.在虚拟机中安装zabbix 安装系统一直托不进虚拟机中;因为没安装Tools组件 点击虚拟机,选择安装VMware Tools 2.配置zabbix

SpringBoot之JWT令牌校验

SpringBoot之JWT令牌校验 本文根据黑马b站springboot3vue3课程 JWT (JSON Web Token)是一种开放标准(RFC 7519),用于在不同实体之间安全地传输信息。它由三个部分组成:头部(Header)…