组合模式
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。 可以通过一个或者多个简单的对象,经过组合之后,生成一个新的对象,原来的对象是这个对象的元素。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
组合模式也可以看成是对某些对象的迭代调用的方式。
组合模式优点:
- 1、高层模块调用简单。
- 2、节点自由增加。
案例分析
下面通过一个大家都熟悉的示例,进行组合模式的初步理解。这个示例来源于往年的软考下午试题。
如下图预构造一个文件目录树,就是我们常用的文件夹和文件的关系类似的。文件夹里面可以包含文件和文件夹,二层文件夹下又可以包含文件和文件夹。其中一层文件夹是二层文件夹和文件的父类;而三层文件夹或者文件又是二层文件夹的子类。类似这样的树状结构, 采用组合设计模式来设计。
分析
性和动作,不管文件和文件夹,都有名称、大小、格式,都可以对文件和文件夹进行操作,包含修改、删除、移动等操作方式。那么可以将这些公共的操作和属性值提取出来一个抽象类。然后用一个子属性来代替下层数据信息。
开发
可以从图中看到,针对公共属性和方法进行提取之后,可以创建一个AbstractFile的抽象类。其中包含:文件夹、文件的名称;添加文件或者文件夹的方法;删除文件或者文件夹的方法;移动文件或者文件夹的方法;获取文件子文件列表的方法。 AbstractFile的抽象类如下:
/** * @ClassName AbstractFile * @Description: * @Author 公众号:Java全栈架构师 * @Version V1.0 **/ public abstract class AbstractFile { protected String fileName; /** * 输出文件、文件夹名称 * */ public void printFileName(){ System.out.println(fileName); } /** * 添加文件或者文件夹 * */ public abstract boolean addChild(AbstractFile abstractFile); /** * 删除文件或者文件夹 * */ public abstract boolean delChild(AbstractFile abstractFile); /** * 移动文件或者文件夹 * */ public abstract boolean removeChild(AbstractFile abstractFile); /** * 获取文件子文件列表 * */ public abstract List<AbstractFile> getChildAbstractFileList(); }
现在编写文件的具体实现类,因为文件没有子文件或文件夹,所以我们子啊获取文件子文件列表的时候,默认就返回null即可。这一点需要注意,和我们业务中的点一样,在编写代码实现的时候,对业务的基本流程和属性是需要一定了解的。其中在方法返回值之前,可以加入实际的业务逻辑,如果成功就返回true;如果处理失败,就返回false。系统会默认的返回false;
文件File的类如下:
/** * @ClassName File * @Description: * @Author 公众号:Java全栈架构师 * @Version V1.0 **/ public class File extends AbstractFile { public File(String name){ this.fileName = name; } @Override public boolean addChild(AbstractFile abstractFile) { return false; } @Override public boolean delChild(AbstractFile abstractFile) { return false; } @Override public boolean removeChild(AbstractFile abstractFile) { return false; } @Override public List<AbstractFile> getChildAbstractFileList() { return null; } }
在创建文件夹的下的子文件或文件的集合的时候,需要注意去获取文件夹下的子文件和子文件夹下的文件和文件夹,需要层层迭代下去,本文作为示例,直接使用new 去创建了。这点需要注意。
Folder文件夹的类如下:
/** * @ClassName Folder * @Description: * @Author 公众号:Java全栈架构师 * @Version V1.0 **/ public class Folder extends AbstractFile { private List<AbstractFile> list; public Folder(String name) { this.fileName = name; this.list = new ArrayList<AbstractFile>(); } @Override public boolean addChild(AbstractFile abstractFile) { this.list.add(abstractFile); return false; } @Override public boolean delChild(AbstractFile abstractFile) { return false; } @Override public boolean removeChild(AbstractFile abstractFile) { return false; } @Override public List<AbstractFile> getChildAbstractFileList() { return list; } }
运行
以上基础模块已经搭建完成了,包含文件夹Folder和文件File两个。他们都有共同的方法。那么现在开始变写一下 遍历目录下的文件和文件夹名称的方法,输出文件名和文件夹名称。
首先初始化模拟一个文件夹及文件的目录格式:其中根目录是:D:\,下面有几个文件夹分别为:“Linux”和“Win”两个,这两个文件夹下还有下层子集文件和文件夹。编写一个输出文件和文件夹名称的方法printFileTree,由于整个目录树都是基于文件夹Folder和文件File两个构建的,因此在输出过程中需要迭代使用printFileTree,具体方法如下:
/** * @ClassName Folder * @Description: * @Author 公众号:Java全栈架构师 * @Version V1.0 **/ public class MainApp { public static void main(String[] args) { AbstractFile rootFolder =new Folder("D:\\"); AbstractFile linuxFolder =new Folder("Linux"); AbstractFile winFolder =new Folder("Win"); AbstractFile jdkFolder =new Folder("jdk"); AbstractFile testFile =new File("test.java"); AbstractFile linuxTestFile =new File("Linux_test.java"); rootFolder.addChild(linuxFolder); rootFolder.addChild(winFolder); winFolder.addChild(jdkFolder); jdkFolder.addChild(testFile); linuxFolder.addChild(linuxTestFile); printFileTree(rootFolder); } public static void printFileTree(AbstractFile abstractFile) { abstractFile.printFileName(); List<AbstractFile> abstractFileList = abstractFile.getChildAbstractFileList(); if (abstractFileList == null) { return; } else { for (AbstractFile abstractFiles : abstractFileList) { printFileTree(abstractFiles); } } } }
运行之后输出结果如下,可以看到文件目录的层级信息。
D:\ Linux Linux_掘金.java Win jdk test_掘金.java
结语
好了,一个简单的组合模式就介绍完成了,学习设计模式的时候,尽量结合熟悉的案例进行分析,这样的话就会有事半功倍的奇效,也更容易理解。大家可以尝试着在项目中使用设计模式的方式进行开发。