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 模式。