Java设计模式-结构型模式设计模式-组合模式
从这一专栏开始将学习设计模式,上课学习和自己总结归纳的笔记将总结出来供大家参考。
参考书籍:《设计模式就该这样学》
其他文章:
文章目录
一、结构型设计模式
在GOF23种设计模式中,有三种类型的设计模式,分别是:创建型设计模式、结构型设计模式、行为型设计模式。
结构型设计模式有:代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式 7 种结构型模式。
类结构型模式:关心类的组合,由多个类组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。
对象结构型模式:关心类与对象的组合,通过关联关系,在一个类中定义另外一个类的实例对象,然后通过该对象调用相应的方法。
二、组合模式
1.组合模式定义
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
结构型设计模式有:代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式 7 种结构型模式。
2.组合模式的角色:
抽象构件(Component):叶子构件与容器构件共同继承的父类或者是共同实现的接口,该角色中包含所有子类共有方法的声明和实现,在抽象构件中定义了管理子构件的方法,新增构件、删除构件、获取构件。
叶子构件(Leaf):表示叶子节点,没有子节点,对于继承父类的管理子节点的方法以抛出异常的方式处理。类似于文件
容器构件(Composite):表示容器节点,包含子节点,子节点可以是容器节点也可以是叶子节点,其提供一个集合来对子节点进行维护,以迭代的方式对子节点进行处理。类似于文件夹
3.组合模式的特点
优点:
节点自由增加。使用了组合模式后,如果想增加一个树枝节点、树叶节点十分简单,只要找到它的父节点就成,非常容易扩展,符合开闭原则,有利于后期维护。
缺点:
树枝树叶直接使用了实现类,这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,限制了接口的影响范围。
适用环境:
1.想表示对象的部分-整体层次结构(树形结构)
2.想用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
4.组合模式的类图
5.组合模式的代码实现
5.1具体案例:
某软件公司欲开发一个杀毒(AntiVirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案:
类图如下:
具体代码实现:
1.抽象构件AbstractFile
public abstract class AbstractFile {
private String filePath;
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public abstract void Add(AbstractFile file);
public abstract void Remove(AbstractFile file);
public abstract AbstractFile GetChild(int i);
public abstract String KillVirus();
}
2.容器构件Folder
public class Folder extends AbstractFile{
//定义集合fileList,用于存储AbstractFile类型的成员
private List<AbstractFile> fileList = new ArrayList<>();
@Override
public void Add(AbstractFile file) {
if (file!=null){
fileList.add(file);
}
}
@Override
public void Remove(AbstractFile file) {
fileList.remove(file);
}
@Override
public AbstractFile GetChild(int i) {
return fileList.get(i);
}
@Override
public String KillVirus() {
//遍历文件夹的所有文件
for (AbstractFile abstractFile : fileList) {
//如果文件夹下面没有文件夹就返回杀毒结果
String result = abstractFile.KillVirus();
if(result!=null){
System.out.println(result);
}
}
return null;
}
}
3.叶子构件TextFile、ImageFile、VideoFile
public class ImageFile extends AbstractFile {
private String name;
@Override
public void Add(AbstractFile file) {
}
@Override
public void Remove(AbstractFile file) {
}
@Override
public AbstractFile GetChild(int i) {
return null;
}
@Override
public String KillVirus() {
System.out.println("对Img文件"+getFilePath()+" 开始杀毒");
if(Math.random()<0.50){
return "感染病毒杀毒成功";
}
return "文件无毒";
}
}
public class VideoFile extends AbstractFile {
private String name;
@Override
public void Add(AbstractFile file) {
}
@Override
public void Remove(AbstractFile file) {
}
@Override
public AbstractFile GetChild(int i) {
return null;
}
@Override
public String KillVirus() {
System.out.println("对Video文件"+getFilePath()+" 开始杀毒");
if(Math.random()<0.40){
return "感染病毒杀毒成功";
}
return "文件无毒";
}
}
public class TextFile extends AbstractFile {
private String name;
@Override
public void Add(AbstractFile file) {
}
@Override
public void Remove(AbstractFile file) {
}
@Override
public AbstractFile GetChild(int i) {
return null;
}
@Override
public String KillVirus() {
System.out.println("对Text文件"+getFilePath()+" 开始杀毒");
if(Math.random()<0.40){
return "感染病毒杀毒成功";
}
return "文件无毒";
}
}
4.客户端测试类
public class Clinet {
/**
* 根据文件名判断文件类型,并返回对应的AbstractFile
* @param filename
* @return
*/
private static AbstractFile getFilefromName(String filename){
if(filename.endsWith(".doc")
||filename.endsWith(".docx")
||filename.endsWith(".xls")
||filename.endsWith(".txt")
||filename.endsWith(".doc")
){
AbstractFile file=new TextFile();
file.setFilePath(filename);
return file;
}
if(filename.endsWith(".jpg")
||filename.endsWith(".png")
){
AbstractFile file=new ImageFile();
file.setFilePath(filename);
return file;
}
if(filename.endsWith(".mp4")
||filename.endsWith(".mp3")
){
AbstractFile file=new VideoFile();
file.setFilePath(filename);
return file;
}
return null;
}
/**
* 将指定的根目录下的文件和文件夹加载到Folder对象中
* @param rootDirectory
* @return
*/
private static Folder loadFilesToFolder(File rootDirectory){
Folder rootFolder = new Folder();
rootFolder.setFilePath(rootDirectory.getAbsolutePath());
//返回当前目录下的文件和子目录
File[] children=rootDirectory.listFiles();
if(children==null){
return null;
}
//遍历文件和子目录
for (File child : children) {
if(child.isDirectory()){
//child是一个子目录,则递归生成Folder对象
Folder folder = loadFilesToFolder(child);
rootFolder.Add(folder);
}
if(child.isFile()){
AbstractFile file = getFilefromName(child.getName());
rootFolder.Add(file);
}
}
return rootFolder;
}
public static void main(String[] args) {
//对D盘下的文件杀毒
File rootfile = new File("D:\\");
Folder folder = loadFilesToFolder(rootfile);
folder.KillVirus();
}
}