Author:Gorit
Date:2021年10月7日
Refer:《图解设计模式》
11.1 Composite 模式
在计算机中,有“文件夹”的概念(在有些操作系统中会成为 “目录”)。文件夹既可以存放文件,也可以存放其他的“文件夹”,在子文件夹中可以继续放文件以及子文件夹。这样文件夹就形成了一种容器结构,递归结构。
这样的话,文件和文件夹就是不同类型的对象,但是它们都“可以被放置在文件夹中”。文件和文件夹有时候又被统称为“目录条目”(directory entry)。在目录条目中,,文件夹 和 文件被当做同一种对象看待(一致性)
例如:我们查找一个文件 或者 目录,其实查找的都是 目录条目
因此,将文件夹与文件都作为目录条目看待一样,将容器和内容作为同一种东西看待,可以方便帮助我们处理问题。
在容器中既可以放入内容,也可以放入小容器,然而小容器中,又可以放置更小的容器。这样形成的容器结构,递归结构
总之:
Composite 模式就是这样的模式,能够使容器与内容一致性,创造出递归结构
11.2 示例程序
这里 Entry 即代表 “目录条目”,这样就实现了 File 和 Directory 类的一致性。
| 名字 | 说明 |
|---|---|
| Entry | 抽象类 |
| File | 表示文件的类 |
| Directory | 表示文件夹的类 |
| FileTreatmentException | 表示向文件中增加 Entry 时发生异常的类 |
| Main | 测试行为 |
Entry 抽象类
package Composite;/*** @Author Gorit* @Date 2021/10/7* @desc 表示目录条目的抽象类, File 和 Directory 为其子类**/public abstract class Entry {public abstract String getName(); // 获取名字public abstract int getSize(); // 获取大小/*** 向文件夹中放入 文件 或 文件夹,该功能由子类 Directory 实现* @param entry* @return*/public Entry add(Entry entry) { // 加入目录条目throw new FileTreatmentException();}public void printList() {printList("");}/*** 显示文件夹内容的一览* @param prefix*/protected abstract void printList(String prefix);// 显示代码类文字public String toString() {return getName() + " (" + getSize() +")";}}
子类 File
package Composite;/*** @Author Gorit* @Date 2021/10/7** @example* new File("readne.md", 1000);**/public class File extends Entry {private String name;private int size;public File(String name, Integer size) {this.name = name;this.size = size;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}// 使用 this,自动调用 toString() 等价于 this.toString()protected void printList(String prefix) {System.out.println(prefix + "/" + this);}}
Directory 目录类
package Composite;import java.util.ArrayList;import java.util.Iterator;/*** @Author Gorit* @Date 2021/10/7* @desc 表示文件夹的类**/public class Dirextory extends Entry {private String name; // 文件夹名称private ArrayList directory = new ArrayList(); // 文件夹中目录的集合public Dirextory(String name) {this.name = name;}public String getName() { // 获取名字return name;}// 这里我们无法确认是目录的实例 还是 文件的实例,所有用 Iterator,都可以得到大小public int getSize() {int size = 0;Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry)it.next();size += entry.getSize();}return size;}public Entry add(Entry entry) {directory.add(entry);return this;}protected void printList(String prefix) { // 显示目录条目一览System.out.println(prefix + "/" + this);Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry)it.next();entry.printList(prefix + "/" + name);}}public void setName(String name) {this.name = name;}public ArrayList getDirectory() {return directory;}public void setDirectory(ArrayList directory) {this.directory = directory;}}
FileTrementException
package Composite;/*** @Author Gorit* @Date 2021/10/7**/public class FileTreatmentException extends RuntimeException {public FileTreatmentException() {}public FileTreatmentException(String msg) {super(msg);}}
Main
package Composite;/*** @Author Gorit* @Date 2021/10/7* 使用 main 创建如下目录树* - root* - bin* - vi* - latex* - tmp* - usr* - yuki* - diary.html* - Composite.java* - hanko* - memo.tax* - tomura* - game.doc* - junk.mail**/public class Main {public static void main(String[] args) {try {System.out.println("Making root entries...");Dirextory rootDir = new Dirextory("root");Dirextory bindDir = new Dirextory("bin");Dirextory tmpDir = new Dirextory("tmp");Dirextory usrDir = new Dirextory("usr");rootDir.add(bindDir);rootDir.add(tmpDir);rootDir.add(usrDir);bindDir.add(new File("vi", 10000));bindDir.add(new File("latex", 20000));rootDir.printList();System.out.println("");System.out.println("Making user entries...");Dirextory yuki = new Dirextory("yuki");Dirextory hanko = new Dirextory("hanko");Dirextory tomura = new Dirextory("tomura");usrDir.add(yuki);usrDir.add(hanko);usrDir.add(tomura);yuki.add(new File("diary.html", 1001));yuki.add(new File("Composite.java", 200));hanko.add(new File("memo.tak", 300));tomura.add(new File("game.doc", 400));tomura.add(new File("junk.mail", 500));rootDir.printList();} catch (FileTreatmentException e) {e.printStackTrace();}}}

1.3 Composite 模式中登场的角色
1.3.1 Leaf(树叶)
表示“内容”的角色。在该橘色中不能放入其他对象。在示例程序中,由 File 类扮演此角色
1.3.2 Composite(复合物)
表示容器的角色。可以在其中放入 Leaf 角色和 Composite 角色。在示例程序中,用 Directory 类扮演此角色
1.3.3 Component
使 Leaf 角色和 Composite 角色具有一致性的角色。Component 角色是Leaf 角色 和 Composte 角色的父类。在此示例程序中,由 Entry 类扮演次橘色
1.3.4 Client
使用 Composite 的角色,在示例程序中,由 Main 类扮演此角色
1.4 拓展思路
1.4.1 多个和单个的一致性
使用 Composite 模式可以使容器 和 内容具有一致性,也可以称其为 多个 和 单个的 一致性
1.4.2 到处存在递归结构
- 视窗系统(一个窗口可以含有一个子窗口)
- 文章列表,各列表可以互相嵌套
- 树结构的数据结构都适用于 Composite 模式。
