领略设计模式的魅力,谈谈组合模式搭配访问者模式

news2025/2/6 23:01:46

组合模式(composite) 我们都知道文件和文件夹的概念,并且文件是可以存放在文件夹中,文件夹中也可以存放其他文件夹。需要设计一个简单的程序来实现文件夹和文件的关系。 实现思路

  1. 文件夹需要存放文件夹和文件,首先想到的是在文件夹中设计两个集合分别来存放文件夹和文件。

  2. 有展示文件路径需求时,不清楚在最里层是文件夹还是文件,所以需要把文件夹和文件当做一个对象来处理会更好,都是条目。所以需要创建一个他们共同的父类。

  3. 对文件夹的设计优化,创建一个集合容器,容器类型是父类,即解决了既要存放文件夹又要存放文件的问题。

有时,将容器和内容作为同一种东西看待,可以很好地帮助我们处理问题,在容器中既可以存放内容,也可以存放小容器,然后在小容器中,又可以继续放入更小的容器。这样,这样就形成了容器结构、递归结构。 创造出这样结构的模式就是组合模式(Composite),能够使容器和内存具有一致性,创造出递归结构的模式。 代码实现 Composite:文件和文件夹的父类 public abstract class Component { public Component(String name) { this.name = name; } /** * 名称 */ protected String name; /** * 展示方法 * @param prefix */ public abstract void show(String prefix); /** * 添加方法 * @param component */ public void add(Component component){ } Folder:文件夹类 public class Folder extends Component { /** * 存放composite的集合 */ private List<Component> componentList = new ArrayList<>(16); public Folder(String name) { super(name); } @Override public void show(String prefix) { prefix += "/"+name; String finalPrefix = prefix; System.out.println(finalPrefix); componentList.forEach(component -> component.show(finalPrefix)); } @Override public void add(Component component) { componentList.add(component); } } file:文件类 public class File extends Component { public File(String name) { super(name); } @Override public void show(String prefix) { System.out.println(prefix+"/"+this.name); } } main方法测试 public static void main(String[] args) { Folder folder = new Folder("根文件夹"); Folder folder1 = new Folder("文件夹1"); folder.add(folder1); File file1 = new File("文件1"); folder1.add(file1); File file2 = new File("文件2"); folder1.add(file2); Folder folder2 = new Folder("文件夹2"); folder.add(folder2); File file3 = new File("文件3"); folder2.add(file3); File file4 = new File("文件4"); folder2.add(file4); File file5 = new File("文件5"); folder2.add(file5); File file6 = new File("文件6"); folder.add(file6); folder.show(""); System.out.println("----------------"); folder1.show(""); } 输出结果:

编辑切换为居中

添加图片注释,不超过 140 字(可选)

类图

编辑切换为居中

添加图片注释,不超过 140 字(可选)

访问者模式 我们在对类中数据结构执行操作A时,一般会在该类中声明一个方法来完成操作A。但是当需要增加另一种操作B时,就需要再增加一个方法。那么在后续不断增加需求过程中,我们就需要不断地去修改这个类,这样就很不符合开闭原则。 那么我们能不能把数据结构和操作分开,当需要增加操作需求时,只需修改操作类呢? 访问模式就可以实现这样的需求。在该模式中,数据结构与处理被分离开来。编写一个表示“访问者”的类来访问数据 中的元素,并把对各元素的处理交给访问者类。这样,当需要增加新的处理时,我们只需要编 的访问者,然后让数据结构可以接受访问者的访问即可。 代码实现 ELement:数据结构接口 public interface Element { void accept(Visitor visitor); } Visitor:访问者接口 public interface Visitor { void visit(Element element); } ConcreteElement:具体数据结构实现类 public class ConcreteElement implements Element{ private String name; private Integer age; @Override public void accept(Visitor visitor) { visitor.visit(this); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "ConcreteElement{" + "name='" + name + '\'' + ", age=" + age + '}'; } } NameVisitor:修改名称属性的访问者 public class NameVisitor implements Visitor { @Override public void visit(Element element) { ConcreteElement concreteElement = (ConcreteElement) element; concreteElement.setName("测试"); } } AgeVisitor:修改年龄属性的访问者 public class AgeVisitor implements Visitor { @Override public void visit(Element element) { ConcreteElement concreteElement = (ConcreteElement) element; concreteElement.setAge(1); } } main方法: public static void main(String[] args) { Element element = new ConcreteElement(); System.out.println(" 没有被访问结果输出: "+element); Visitor visitor = new NameVisitor(); element.accept(visitor); System.out.println(" 被NameVisitor访问结果输出: "+element); visitor = new AgeVisitor(); element.accept(visitor); System.out.println(" 被AgeVisitor访问结果输出: "+element); } 数据结果:

通过上面的代码实现,可以看到ConcreteElement通过accept实现了对访问者动态变更,通过传入不同的访问者实现类不同的操作需求,后期因需求的增加只需增加不同的访问者。 类图

编辑切换为居中

添加图片注释,不超过 140 字(可选)

俩个模式搭配干活 浅尝 需求 在组合模式中,完成了一个文件夹的设计。现在需要增加一个需求:对当前文件夹中的文件做名称修改。 这个需求其实很简单,首先想到应该就是遍历文件夹修改里面的文件名称。 那么有没有更优雅的方式呢?试试访问者模式 代码实现 下面贴入的代码已省略在组合模式已有的代码。 文件相关代码: public interface Element { void accept(Visitor visitor); } public abstract class Component implements Element{} public class File extends Component { @Override public void accept(Visitor visitor) { visitor.visit(this); } } public class Folder extends Component { @Override public void accept(Visitor visitor) { componentList.stream().forEach(component -> visitor.visit(component)); } } 访问者相关代码: public interface Visitor { void visit(Element element); } public class UpdateFileNameVisitor implements Visitor { @Override public void visit(Element element) { if(element instanceof Folder){ element.accept(this); }else{ File file = (File) element; file.setName("visitor update-"+file.getName()); } } } main方法 public static void main(String[] args) { // 省略已有代码 folder.show(""); System.out.println("----------------"); Visitor visitor = new UpdateFileNameVisitor(); folder.accept(visitor); folder.show(""); } 结果:

编辑切换为居中

添加图片注释,不超过 140 字(可选)

深入 在我们日常业务代码中,经常会出现树型结构数据。比商品分类,权限等,一般涉及到这样的数据结构可以考虑使用组合模式+访问模式来处理需求。 需求 在商品分类处理中

  1. 实现商品分类树的查询 。

  2. 实现商品分类的删除,并删除他的子类。

代码实现 商品分类数据结构 public class CommodityClass { /** * 标识 */ private Long id; /** * 名称 */ private String name; /** * 父分类ID */ private Long parentId; /** * 商品分类子集 */ private List<CommodityClass> Children; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<CommodityClass> getChildren() { return Children; } public void setChildren(List<CommodityClass> children) { Children = children; } public Long getParentId() { return parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } public <T> T execute(CommodityClassOperation<T> commodityClassOperation){ return commodityClassOperation.doExecute(this); } @Override public String toString() { return "CommodityClass{" + "id=" + id + ", name='" + name + '\'' + ", parentId=" + parentId + ", Children=" + Children + '}'; } } service层处理类 public interface CommodityClassService { /** * 查询商品分类结构树 * @param commodityClassId 商品分类ID * @return 商品分类结构树 */ CommodityClass getAllCommodityClassServiceById(Long commodityClassId); /** * 删除商品分类 * @param commodityClassId 商品分类ID * @return true 成功 false 失败 */ Boolean deleteAllCommodityClassServiceById(Long commodityClassId); } @Service public class CommodityClassServiceImpl implements CommodityClassService { @Autowired private QueryCommodityClassOperation queryCommodityClassOperation; @Autowired private DeleteCommodityClassOperation deleteCommodityClassOperation; @Autowired private CommodityClassDAO commodityClassDAO; /** * 查询商品分类结构树 * @param commodityClassId 商品分类ID * @return 商品分类结构树 */ @Override public CommodityClass getAllCommodityClassServiceById(Long commodityClassId){ CommodityClass commodityClassServiceById = commodityClassDAO.getCommodityClassServiceById(commodityClassId); queryCommodityClassOperation.doExecute(commodityClassServiceById); return commodityClassServiceById; } /** * 删除商品分类 * @param commodityClassId 商品分类ID * @return true 成功 false 失败 */ @Override public Boolean deleteAllCommodityClassServiceById(Long commodityClassId){ CommodityClass commodityClassServiceById = commodityClassDAO.getCommodityClassServiceById(commodityClassId); return deleteCommodityClassOperation.doExecute(commodityClassServiceById); } } 访问者类 public interface CommodityClassOperation<T> { T doExecute(CommodityClass commodityClass); } @Component public class QueryCommodityClassOperation implements CommodityClassOperation<Boolean> { @Autowired private CommodityClassDAO commodityClassDAO; @Override public Boolean doExecute(CommodityClass commodityClass) { List<CommodityClass> children = commodityClassDAO.listCommodityClassServiceByParentId(commodityClass.getId()); if(!CollectionUtils.isEmpty(children)){ children.stream().forEach(commodityClass1 -> commodityClass1.execute(this)); } commodityClass.setChildren(children); return true; } } @Component public class DeleteCommodityClassOperation implements CommodityClassOperation<Boolean> { @Autowired private CommodityClassDAO commodityClassDAO; @Override public Boolean doExecute(CommodityClass commodityClass) { List<CommodityClass> children = commodityClassDAO.listCommodityClassServiceByParentId(commodityClass.getId()); if(!CollectionUtils.isEmpty(children)){ children.stream().forEach(commodityClass1 -> commodityClass1.execute(this)); } commodityClassDAO.deletCommodityClassServiceById(commodityClass.getId()); return true; } } DAO层查询数据库已省略可以自行实现。

                                               资源获取:

大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻微信公众号获取联系方式👇🏻👇🏻👇🏻

 精彩专栏推荐订阅:下方专栏👇🏻👇🏻👇🏻👇🏻

每天学四小时:Java+Spring+JVM+分布式高并发,架构师指日可待

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

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

相关文章

大恒普信携手昇思推出眼健康AI智能分析系统,为眼科医疗行业数字化转型升级助力

电子屏幕时代&#xff0c;人们的用眼强度不断增加&#xff0c;各种眼底疾病也开始广泛出现&#xff0c;如青光眼、病理性近视、糖尿病视网膜病变等&#xff0c;严重时可致盲。其实&#xff0c;对大多数眼底疾病而言&#xff0c;如果能早发现、早治疗&#xff0c;就可以很好地预…

把d盘的文件删除了,怎么恢复?d盘的文件删除了怎么找回

把d盘的文件删除了,怎么恢复&#xff1f;通常&#xff0c;我们删除d盘文件的情况是不一样的&#xff0c;可能是通过不同的方法删除的&#xff0c;针对不同的删除方式&#xff0c;那么要恢复这些文件的方法也是不同的&#xff0c;小编按删除方式和时间来给大家进行详细的讲解。 …

TensorFlow手动搭建神经网络实现鸢尾花分类

步骤 准备数据 搭建网络 定义神经网络中所有可训练参数 参数优化 嵌套循环迭代&#xff0c;with结构更新参数&#xff0c;显示当前loss 测试效果 计算当前参数前向传播后的准确率&#xff0c;显示当前acc acc/loss可视化 这里使用一个最简单的网络实现鸢尾花分类 完整代码…

一文搞定 Postman 接口自动化测试

本文适合已经掌握 Postman 基本用法的读者&#xff0c;即对接口相关概念有一定了解、已经会使用 Postman 进行模拟请求等基本操作。 工作环境与版本&#xff1a; Window 7&#xff08;64位&#xff09; Postman &#xff08;Chrome App v5.5.3&#xff09; P.S. 不同版本页面…

Vue中在组件中单独使用this

目录 &#x1f53d; 全局注册 &#x1f53d; 局部注册 &#x1f53d; 组件使用总结 &#x1f53d; 全局注册 1、Vue.prototype 在多个地方都需要使用但不想污染全局作用域的情况下&#xff0c;这样定义&#xff0c;在每个 Vue 实例中都可用。$ 表示这是一个在 Vue 所有实…

Allegro如何查看PCB进度百分比操作指导

Allegro如何查看PCB进度百分比操作指导 Allegro支持实时查看PCB进度百分比,让设计者实时了解设计进度,具体操作如下 选择Display-StatusUnrouted connections这里就是就剩下未完成的百分比,如果是0,代表已经完成除了可以在这里快捷的查看,也可以通过报表实现,选择Tools-r…

InstructPix2Pix: 随口修图

InstructPix2Pix Learning to Follow Image Editing Instructions是一篇非常有意思的文章&#xff0c;有意思说的是效果&#xff0c;要做出论文的效果过程并没那么顺利。首先需要微调GPT3模型&#xff0c;这个花钱花力气&#xff0c;在之前的文章里已经提过&#xff0c;可以参考…

RedisSon分布式锁 源码解析,在 java 中使用 redis + lua 做秒杀

1. RedisSon 分布式锁 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.17.0</version> </dependency>spring:profiles:active: devredis:cluster:nodes: 192…

Mac OSX 安装 MongoDB

1&#xff0c;简介 MongoDB是由C语言编写&#xff0c;开源而且基于分布式文件存储的介于关系数据库和非关系数据库之间的产品&#xff1b;在高负载的情况下&#xff0c;通过添加更多节点保证服务器性能&#xff1b;旨在为WEB应用提供可扩展的高性能数据存储解决方案&#xff1…

Copy-Paste

在2D视觉目标检测领域&#xff0c;由相似目标之间的重叠引起的拥挤是普遍存在的挑战。 文章地址&#xff1a;https://arxiv.org/pdf/2211.12110.pdf 研究者首先强调了拥挤问题的两个主要影响&#xff1a;1&#xff09;IoU置信度相关干扰&#xff08;ICD&#xff09;和2&#…

桥接模式

文章目录桥接模式1.桥接模式的本质2.何时选用桥接模式3.优缺点4.桥接模式的结构5.实现模拟消息发送MVC在桥接模式的体现桥接模式 桥接模式实质就是分离抽象和实现&#xff0c;抽象部分有多种&#xff0c;实现部分有多种&#xff0c;耦合在一起很难扩展&#xff0c;将其分离开来…

excel如何排序?两个方法汇总

排序是Excel中最常用的功能之一&#xff0c;也是数据分类和汇总操作的重要前提。excel如何排序&#xff1f;本文介绍如何给Excel里面的数据进行排序&#xff0c;方法很简单。感兴趣的朋友&#xff0c;赶紧来看看吧&#xff01; 操作环境&#xff1a; 演示机型&#xff1a;Dell …

PostgreSQL 导入 SLS,从业务到监控数据

日志服务SLS数据导入简介 日志服务SLS是云原生观测和分析平台&#xff0c;为Log、Metric、Trace等数据提供大规模、低成本、实时的平台化服务。日志服务是提供一站式数据采集、加工、查询与分析、可视化、告警、消费与投递等功能。全面提升在研发、运维、运营、安全等场景的数…

web常见的攻击方式有哪些,以及如何进行防御?

一、是什么 Web攻击&#xff08;WebAttack&#xff09;是针对用户上网行为或网站服务器等设备进行攻击的行为 如植入恶意代码&#xff0c;修改网站权限&#xff0c;获取网站用户隐私信息等等 Web应用程序的安全性是任何基于Web业务的重要组成部分 确保Web应用程序安全十分重…

python中的模块与包详解

目录 一.什么是模块 二.模块的导入 1.import 模块名 2.from 模块名 import 功能名 3.from 模块名 import * 4.as定义别名 模块导入总结 三.自定义模块 制作自定义模块 用pycharm演示 测试模块_ _main_ _变量的作用 演示 ‘_ _all_ _’变量 自定义模块小结 四.python中的包…

Flink集成Seatunnel

安装包下载 相关包的下载地址 Apache SeaTunnel | Apache SeaTunnel Apache Flink: Downloads 解压&#xff08;注意下载scala_2.11&#xff09; tar -zxvf flink-1.13.6-bin-scala_2.11.tgz -C ../module/ Yarn模式部署 环境准备 sudo vi /etc/profile.d/my_env.sh 修…

中国清洁清洗行业等级资质

中国商业企业管理协会清洁服务商专业委员会——“中清委”&#xff08;以下简称评定单位&#xff09;承担组织等级清洁清洗服务机构评定工作。 申请资料 (1)专业清洁清洗服务机构等级评定申请表&#xff08;附录B&#xff09;&#xff1b; (2)法人代表资格证明&#xff1…

小林Coding阅读笔记:操作系统篇之硬件结构,伪共享问题及CPU的任务执行

前言 参考/导流&#xff1a; 小林coding - 2.5 CPU 是如何执行任务的&#xff1f;学习意义 底层基础知识&#xff0c;了解CPU执行过程&#xff0c;让上层编码有效并发问题处理、思考理解调度策略、思想借鉴分析 相关说明 该篇博文是个人阅读的重要梳理&#xff0c;仅做简单参…

Transformer实现以及Pytorch源码解读(一)-数据输入篇

目标 以词性标注任务为例子&#xff0c;实现Transformer&#xff0c;并分析实现Pytorch的源码解读。 数据准备 所选的数据为nltk数据工具中的treebank数据集。treebank数据集的样子如以下两幅图所示&#xff1a; 该数据集中解释变量为若干句完整的句子&#xff1a; 被解释变…

Docker-DockerFile制定镜像

什么是DockerFile&#xff1f; DockerFile是一个用来编写Docker镜像的文本文件&#xff0c;文本内容包含了一条条构建镜像所需要的指令和说明。DockerFile就想要一个脚本文件一样。把我们想要执行的操作放到文本文件里&#xff0c;一键执行。这样我们就可以复用这个DockerFile…