设计模式-组合模式在Java中的使用示例-杀毒软件针对文件和文件夹进行杀毒

场景

组合模式

组合模式(Composite Pattern):

组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。

组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,

组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。

在组合模式中引入了抽象构件类Component,它是所有容器类和叶子类的公共父类,客户端针对

Component进行编程。组合模式结构如图

包含角色:

 Component(抽象构件):

它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。

在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

 Leaf(叶子构件):

它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。

对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。

Composite(容器构件):

它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,

它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,

在其业务方法中可以递归调用其子节点的业务方法。

组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,

无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,

在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

如果不使用组合模式,客户端代码将过多地依赖于容器对象复杂的内部实现结构,容器对象内部实现结构的变化将引起客户

代码的频繁变化,带来了代码维护复杂、可扩展性差等弊端。组合模式的引入将在一定程度上解决这些问题。

组合模式-树形结构的处理

树形结构在软件中随处可见,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组

织结构等等,如何运用面向对象的方式来处理这种树形结构是组合模式需要解决的问题,组合模式通

过一种巧妙的设计方案使得用户可以一致性地处理整个树形结构或者树形结构的一部分,也可以一致性地

处理树形结构中的叶子节点(不包含子节点的节点)和容器节点(包含子节点的节点)。

欲开发一个杀毒(AntiVirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。

该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,

例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主

实现

不使用组合模式

如果不使用组合模式,可能会这样实现

1、新建图像文件类

//图像文件类
public class ImageFile {private String name;public ImageFile(String name) {this.name = name;}public void killVirus(){System.out.println("对图像文件"+name+"进行杀毒");}
}

2、新建文本文件类

//文本文件类
public class TextFile {private String name;public TextFile(String name) {this.name = name;}public void killVirus(){System.out.println("对文本文件"+name+"进行杀毒");}
}

3、新建文件夹类

//文件夹类
public class Folder {private String name;//用于存储Folder类型的成员private ArrayList<Folder> folderList = new ArrayList<Folder>();//用于存储ImageFile类型的成员private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>();//用于存储TextFile类型的成员private ArrayList<TextFile> textList = new ArrayList<TextFile>();public Folder(String name) {this.name = name;}//增加新的Folder类型的成员public void addFolder(Folder folder){folderList.add(folder);}//增加新的ImageFile类型的成员public void addImageFile(ImageFile imageFile){imageList.add(imageFile);}//增加新的TextFile类型的成员public void addTextFile(TextFile textFile){textList.add(textFile);}//此处省略三种类型的删除成员的方法、获取成员的方法public void killVirus(){System.out.println("对文件夹"+name+"进行杀毒");//如果是Folder类型的成员,递归调用Folder的killVirus方法for (Object obj : folderList) {((Folder)obj).killVirus();}//如果是ImageFile类型for (Object obj : imageList) {((ImageFile)obj).killVirus();}//如果是TextFile类型for (Object obj : textList) {((TextFile)obj).killVirus();}}
}

4、客户端调用示例如下

    public static void main(String[] args) {Folder folder1,folder2,folder3;folder1 = new Folder("工作资料");folder2 = new Folder("图片文件");folder3 = new Folder("文本文件");ImageFile imageFile1,imageFile2;imageFile1 = new ImageFile("背景图.jpg");imageFile2 = new ImageFile("示例图.gif");TextFile textFile1,textFile2;textFile1 = new TextFile("接口.doc");textFile2 = new TextFile("示例.txt");folder2.addImageFile(imageFile1);folder2.addImageFile(imageFile2);folder3.addTextFile(textFile1);folder3.addTextFile(textFile2);folder1.addFolder(folder2);folder1.addFolder(folder3);folder1.killVirus();}
}

分析存在的问题

(1) 文件夹类Folder的设计和实现都非常复杂,需要定义多个集合存储不同类型的成员,而且需要针对不同的成员

提供增加、删除和获取等管理和访问成员的方法,存在大量的冗余代码,系统维护较为困难;

(2) 由于系统没有提供抽象层,客户端代码必须有区别地对待充当容器的文件夹Folder和充当叶子

的ImageFile和TextFile,无法统一对它们进行处理;

(3) 系统的灵活性和可扩展性差,如果需要增加新的类型的叶子和容器都需要对原有代码进行修改,

例如如果需要在系统中增加一种新类型的视频文件VideoFile,则必须修改Folder类的源代码,

否则无法在文件夹中添加视频文件。

使用组合模式改造

1、新建抽象文件类作为抽象构件

//抽象文件类:抽象构件
abstract class AbstractFile
{public abstract void add(AbstractFile file);public abstract void remove(AbstractFile file);public abstract AbstractFile getChild(int i);public abstract void killVirus();
}

2、新建图像文件类作为叶子构件

//图像文件类:叶子构件
public class ImageFile extends AbstractFile {private String name;public ImageFile(String name) {this.name = name;}@Overridepublic void add(AbstractFile file) {System.out.println("不支持该方法");}@Overridepublic void remove(AbstractFile file) {System.out.println("不支持该方法");}@Overridepublic AbstractFile getChild(int i) {System.out.println("不支持该方法");return null;}@Overridepublic void killVirus() {System.out.println("对图像文件"+name+"进行杀毒");}
}

3、新建文本文件类

public class TextFile extends AbstractFile{private String name;public TextFile(String name) {this.name = name;}@Overridepublic void add(AbstractFile file) {System.out.println("不支持该方法");}@Overridepublic void remove(AbstractFile file) {System.out.println("不支持该方法");}@Overridepublic AbstractFile getChild(int i) {System.out.println("不支持该方法");return null;}@Overridepublic void killVirus() {System.out.println("对文本文件"+name+"进行杀毒");}
}

4、新建文件夹类

import java.util.ArrayList;public class Folder extends AbstractFile{//用于存储AbstractFile类型的成员private ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>();private String name;public Folder(String name) {this.name = name;}@Overridepublic void add(AbstractFile file) {fileList.add(file);}@Overridepublic void remove(AbstractFile file) {fileList.remove(file);}@Overridepublic AbstractFile getChild(int i) {return fileList.get(i);}@Overridepublic void killVirus() {System.out.println("对文件夹"+name+"进行杀毒");for (Object obj : fileList) {((AbstractFile)obj).killVirus();}}
}

5、客户端调用方式如下

public class Client {public static void main(String[] args) {//针对抽象构件编程AbstractFile file1,file2,file3,file4,folder1,folder2,folder3;folder1 = new Folder("工作资料");folder2 = new Folder("图片文件");folder3 = new Folder("文本文件");file1 = new ImageFile("背景图.jpg");file2 = new ImageFile("示例图.gif");file3 = new TextFile("接口.doc");file4 = new TextFile("示例.txt");folder2.add(file1);folder2.add(file2);folder3.add(file3);folder3.add(file4);folder1.add(folder2);folder1.add(folder3);folder1.killVirus();}
}

6、总结

通过引入组合模式,杀毒软件具有良好的可扩展性,在增加新的文件类型时,无须修改现有类库代码,

只需增加一个新的文件类作为AbstractFile类的子类即可,但是由于在AbstractFile中声明了大量用于管理和访问成员构件的方法,

例如add()、remove()等方法,我们不得不在新增的文件类中实现这些方法,提供对应的错误提示和异常处理。

所以代码还可简化将叶子构件的add()、remove()等方法的实现代码移至AbstractFile类中,由AbstractFile提供统一的默认实现。

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

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

相关文章

排序算法之冒泡排序详解-python版

冒泡排序&#xff1a;通过比较2个相邻元素之间的大小&#xff0c;交换元素顺序&#xff0c;从而达到排序目的。 从百度百科摘抄下来的冒泡排序原理如下&#xff1a; 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 对每一对相邻元素做同样的工作&#xf…

elementUI 非表单格式的校验

在普通表单中对输入框、选择框都有校验案例。 但是在自定义非空中如何进行校验官网并没有说明 关键代码 clearValidate 方法清除校验 this.$refs.formValue.clearValidate(signinimg) 使用案例 <template><div class"stylebg"><Tabs icons"el-…

MySQL原理探索——31 误删数据后除了跑路,还能怎么办

在前面几篇文章中&#xff0c;介绍了 MySQL 的高可用架构。当然&#xff0c;传统的高可用架构是不能预防误删数据的&#xff0c;因为主库的一个 drop table 命令&#xff0c;会通过 binlog 传给所有从库和级联从库&#xff0c;进而导致整个集群的实例都会执行这个命令。 虽然我…

blender 阵列修改器

效果 tab 键进入编辑模式&#xff0c;全选制作好的模型&#xff0c;gx 移动模型置于游标原点&#xff1b; 阵列修改器&#xff1a; 相对偏移&#xff1a;以物体的长宽高为比例&#xff0c;调整x y z 的数值&#xff0c;在 x y z 方向上做不同比例的偏移&#xff1b; 恒定偏移…

C#安装.Net平台科学计算库Math.Net Numerics

工作的时候需要使用到C#的Math.Net库来进行计算。 Math.Net库涵盖的主题包括特殊函数&#xff0c;线性代数&#xff0c;概率模型&#xff0c;随机数&#xff0c;插值&#xff0c;积分&#xff0c;回归&#xff0c;优化问题等。 这里记录一下&#xff0c;安装Math.Net库的过程…

Linux 部署Vue+Spring Boot项目

部署Vue Spring Boot项目 安装redis wget http://download.redis.io/releases/redis-4.0.8.tar.gz tar -zxvf redis-4.0.8.tar.gz yum install gcc-c make make install如果出现下面的问题&#xff1a; yum install tcl make testredis-server myconifg/redis.conf输入客户端…

[JVM] 2. 类加载子系统(1)-- 内存结构、类加载子系统概述

一、内存结构 类加载子系统的职责是&#xff1a;加载class文件到内存中。 完整的内存结构如下&#xff1a; 二、类加载过程 类加载过程总体分为Loading&#xff08;加载&#xff09;、Linking&#xff08;链接&#xff09;、Initialization&#xff08;初始化&#xff09;三…

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 五)

Styles装饰器&#xff1a;定义组件重用样式 如果每个组件的样式都需要单独设置&#xff0c;在开发过程中会出现大量代码在进行重复样式设置&#xff0c;虽然可以复制粘贴&#xff0c;但为了代码简洁性和后续方便维护&#xff0c;我们推出了可以提炼公共样式进行复用的装饰器St…

一文读懂 MySQL 中的索引

文章目录 1. 索引概述1.1 索引概述1.2 优点1.3 缺点1.6 常见索引概念1.6.1 聚簇索引1.6.2 二级索引&#xff08;辅助索引、非聚簇索引&#xff09;1.6.3 联合索引 1.8 MyISAM索引的原理1.9 MyISAM 与 InnoDB对比1.10 索引的代价 2. 索引的创建与设计原则2.1 索引的声明与使用2.…

一文9个步骤带你从0到1入门接口自动化测试!

1.请问你是如何做接口测试的&#xff1f; 大体来说&#xff0c;经历以下过程&#xff1a;接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。 具体来说&#xff0c;接口测试流程分成以下九步&#xff1a; 第一步&…

6.3.6 利用Wireshark进行协议分析(六)----网页提取过程的协议分析

6.3.6 利用Wireshark进行协议分析&#xff08;六&#xff09;----网页提取过程的协议分析 利用Wireshark捕获网页访问过程中产生的应用协议报文&#xff0c;还原Web服务中报文的交互过程&#xff0c;为了防止网页直接从本地缓存中获取&#xff0c;我们首先需要清空浏览器保存的…

el-date-picker 宽度溢出浏览器问题

原文链接&#xff1a; el-date-picker 宽度溢出浏览器问题 问题由来 <el-date-picker v-model"Time" type"datetimerange"range-separator"至"start-placeholder"年/月/日 时:分:秒"end-placeholder"年/月/日 时:分:秒"…