对装饰器模式的理解

目录

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

一、场景

  • 在Java中,提供某种功能/服务给客户端(应用层的类)去使用,一般都是先定义接口,然后实现该接口。
// 应用层
@Component
public class DataSourceApplication {@Autowiredprivate 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 {@Overridepublic void writeData(String data) {System.out.println("[FileDataSourceImpl.writeData]");}@Overridepublic String readData() {System.out.println("[FileDataSourceImpl.readData]");return "FileDataSourceImpl.readData";}
}
  • 这时候,产品经理要求写入数据前要先加密,读出数据后要解密。

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

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

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

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

@Primary
@Service
public class EncryptFileDataSourceImpl extends FileDataSourceImpl {@Overridepublic void writeData(String data) {// 对数据进行加密String encryptedData = null;...super.writeData(encryptedData);System.out.println("[EncryptFileDataSourceImpl.writeData]");}@Overridepublic 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 {@Overridepublic void writeData(String data) {System.out.println("[FileDataSourceImpl.writeData] 写入数据");}@Overridepublic String readData() {return "[FileDataSourceImpl.readData] 读取数据";}
}public class EncryptDataSourceImpl implements DataSource {private DataSource dataSource;public EncryptDataSourceImpl(DataSource dataSource) {this.dataSource = dataSource;}@Overridepublic void writeData(String data) {System.out.println("[EncryptFileDataSourceImpl.writeData] 加密数据");dataSource.writeData(data);}@Overridepublic 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;}@Overridepublic void writeData(String data) {System.out.println("[CompressDataSourceImpl.writeData] 压缩数据");dataSource.writeData(data);}@Overridepublic String readData() {System.out.println(dataSource.readData());return "[CompressDataSourceImpl.readData] 解压数据";}
}
@Configuration
public class DataSourceConfig {@Beanpublic 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 {@Beanpublic 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.mzph.cn/news/822972.shtml

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

相关文章

Linux中进程和计划任务

一.程序 1.什么是程序 (1)是一组计算机能识别和执行的指令,运行于电子计算机上,满足人们某种需求的信息化工具 (2)用于描述进程要完成的功能,是控制进程执行的指令集 二.进程 1.什么是进程…

Go 单元测试之HTTP请求与API测试

文章目录 一、httptest1.1 前置代码准备1.2 介绍1.3 基本用法 二、gock2.1介绍2.2 安装2.3 基本使用2.4 举个例子2.4.1 前置代码2.4.2 测试用例 一、httptest 1.1 前置代码准备 假设我们的业务逻辑是搭建一个http server端,对外提供HTTP服务。用来处理用户登录请求…

每日OJ题_01背包④_力扣1049. 最后一块石头的重量 II

目录 力扣1049. 最后一块石头的重量 II 问题解析 解析代码 滚动数组优化代码 力扣1049. 最后一块石头的重量 II 1049. 最后一块石头的重量 II 难度 中等 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合,从…

数字乳腺癌组织病理学图像分类的Vision Transformer及其变体

Vision Transformer作为一种基于自注意力机制的高效图像分类工具被提出。近年来出现了基于Poolingbased Vision Transformer (PiT)、卷积视觉变压器(CvT)、CrossFormer、CrossViT、NesT、MaxViT和分离式视觉变压器(SepViT)等新模型。 它们被用于BreakHis和IDC数据集上的图像分…

软件项目管理 - PERT 图

文章目录 1 概述1.1 PERT 图1.2 基础概念 2 相关计算2.1 最早时刻2.2 最迟时刻2.3 关键路径2.4 松弛时间 1 概述 1.1 PERT 图 PERT:Program Evaluation and Review Technique(项目评估与评审技术) PERT 图是一个有向图,图中的箭…

【c++】stack和queue使用 stack和queue模拟实现

主页:醋溜马桶圈-CSDN博客 专栏:c_醋溜马桶圈的博客-CSDN博客 gitee:mnxcc (mnxcc) - Gitee.com 目录 1. stack的介绍和使用 1.1 stack的介绍 1.2 stack的使用 1.3 stack的模拟实现 2. queue的介绍和使用 2.1 queue的介绍 2.2 queue的…

SpringBoot之JWT令牌校验

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

如何实现音乐音频合并?分享3种简单的合并技巧!音频合并的方法

音乐合并,作为一种音乐创作与编辑的手法,已经逐渐在音乐制作领域占据了一席之地。音乐合并不仅是对音乐元素的重新组合,更是对音乐内涵的深化和拓展。它可以将不同的音乐风格和元素巧妙地融合在一起,创造出全新的听觉体验。 一&a…

DonkeyDocker-v1-0渗透思路

MY_BLOG https://xyaxxya.github.io/2024/04/13/DonkeyDocker-v1-0%E6%B8%97%E9%80%8F%E6%80%9D%E8%B7%AF/ date: 2024-04-13 19:15:10 tags: 内网渗透Dockerfile categories: 内网渗透vulnhub 靶机下载地址 https://www.vulnhub.com/entry/donkeydocker-1,189/ 靶机IP&a…

芯片设计围炉札记

文章目录 语言Verilog 和 VHDL 区别 芯片验证 语言 System Verilog的概念以及与verilog的对比 IC 设计软件分析 Verilog 和 VHDL 区别 Verilog HDL 和 VHDL 的区别如下: 语法结构:Verilog的语法结构类似于C语言,而VHDL的语法结构则更接近…

苍穹外卖学习记录(一)

1.JWT令牌认证 JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。 JWT是目前最常用的一种令牌规范,它最…

QT-编译报库错误(LF/CRLF)

QT-安装后环境问题记录 版本和环境问题 版本和环境 QT5.15.2 Windows10 QT Creator 问题 在QT夸端开发的项目中 ,使用QTCreator打开项目pro文件,编译报出很多系统库 及本地文件中的一些问题,具体如图: 后续,我以为…

数字人结合动捕设备化身虚拟主持人如何玩转大型活动?

由十五运会和残特奥会广州赛区执委会、广州市政府新闻办、广州市科学技术局联合举办的“科技赋能 畅想全运”故事会上,数字人“小运”结合动捕设备化身虚拟主持人惊喜亮相,与真人主持人趣味互动,并向大众介绍了其后续将在大运会上给运动员、工…

redis的主从复制(docker方式快速入门和实战)

目录 一、主从复制简介 二、配置主从服务器 2.1使用配置文件的形式来主从复制 2.2使用纯代码的方式来进行主从复制; 2.3脱离主服务器 三、一些注意事项 一、主从复制简介 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器…

APEX开发过程中需要注意的小细节5.5

oracle保留小数点后两位的函数 在日常开发中经常用到百分比做数据对比,但是有可能得到的数据是一个多位小数,结果如下所示: 如果想截取部分小数如保留小数点后两位可以怎么做呢? 在Oracle中,可以使用ROUND函数来四舍…

51单片机入门_江协科技_31~32_OB记录的自学笔记_LCD1602液晶显示屏

31. LCD1602 31.1. LCD1602介绍 •LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符 •显示容量:162个字符&#xff0c…

二、Maven安装

Maven安装 一、Centos7.9安装1.下载2.安装3.设置国内镜像4.设置maven安装路径 一、Centos7.9安装 1.下载 第一种:官网下载最新版本:http://maven.apache.org/download.cgi第二种:其他版本下载:https://archive.apache.org/dist/…

数据中心的新动脉:探索ADOP 800G MPO/MTP单模光纤跳线

在数据传输领域,随着技术的不断进步,对于更快、更高效的传输解决方案的需求日益增长。ADOP 800G MPO/MTP单模光纤跳线便是在这样的背景下应运而生的产品。本文将为您提供一个全面而详细的介绍,带您深入了解这款创新产品的特点、优势以及应用场…

国产主流数据库存储类型简析

国产数据库在技术架构上主要分为集中式、基于中间件分布式和原生分布式架构,衍生出集中式架构和分布式架构。那么在这些部署架构中,从数据分布的视角来看,在数据库中数据分布的形态是怎样的。本文将简要分析OceanBase、PolarDB、OpenGauss、G…