开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情
组合模式
概叙
在计算机文件系统中,文件夹既可以放文件,也可以放文件夹(子文件夹)。在子文件夹中,一样既可以放文件,也可以放子文件夹。可以说,文件夹形成了一种容器结构、递归结构。
虽然文件和文件夹是不同类型,但是都可以被放入文件夹中,。文件和文件夹有时也被统称为“目录条目”。在目录条目中,文件夹和文件被当作同一种对象看待(即一致性)。
将文件夹和文件都当作目录条目看待,将容器和内容作为同一种东西看待,可以帮助我们方便的处理问题。在容器中既可以放入内容,也可以放入小容器,在小容器中,又可以放入更小的容器。。。。。这样,就形成了容器结构、递归结构。
Composite模式就是用于创造这种结构的模式。能够时容器与内容具有一致性,创造递归结构。
示例程序
类的一览表
Entity抽象类,用来实现File和Directory的一致性File表示文件的类Directory表示文件夹的类Main测试入口
类图
代码
Entry类
抽象父类,File和Directory是其子类。
通过getName()获取名字,getSize()获取大小。
add()方法是向文件夹中放入文件(此处抛出异常,Directory需要将其覆盖)。
printList()为外部调用,printList(String prefix)为子类调用。
public abstract class Entity {
public abstract String getName(); //获取名字
public abstract int getSize(); //获取大小
public Entity add(Entity entity) throws Exception {
throw new RuntimeException("运行异常"); //加入目录条目
}
public void printList() {
printList(""); //显示目录条目一览
}
protected abstract void printList(String prefix); //为一览加上前缀
@Override
public String toString() { //显示代表类文字
return getName() + "(" + getSize() + ")";
}
}
File类
name表示文件名,size表示文件大小。
实现printList(String prefix) 方法。
public class File extends Entity {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
}
}
Directory类
表示文件夹的类,是Entity的子类。
name表示文件夹名,directory保存文件夹的目录条目(指定泛型Entity)。
getSize(),通过计算得出大小。size += entity.getSize()。entity不论是File的实例还是Directory的实例,我们都可以通过getSize得到它的大小。 这就是Composite模式的特征——“容器与内容的一致性”。
如果entity是Directory的实例,调用getSize()会将该文件夹下的所有目录条目大小加起来。如果还有子文件夹,又会调用getSize()方法,形成递归调用。getSize()方法的递归调用与Composite模式的结构是相对应的。
add方法用于向文件夹中加入子文件夹或文件。
public class Directory extends Entity {
private String name;
private ArrayList<Entity> directory = new ArrayList();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
int size = 0;
Iterator<Entity> it = directory.iterator();
while (it.hasNext()) {
Entity entity = it.next();
size += entity.getSize();
}
return size;
}
@Override
public Entity add(Entity entity) throws Exception {
directory.add(entity);
return this;
}
@Override
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
Iterator<Entity> it = directory.iterator();
while (it.hasNext()) {
Entity entity = it.next();
entity.printList(prefix + "/" + name);
}
}
}
Main类
public class Main {
public static void main(String[] args) {
try {
System.out.println("making root entries......");
Directory rootDir = new Directory("root");
Directory binDir = new Directory("bin");
Directory tmpDir = new Directory("tmp");
Directory usrDir = new Directory("usr");
rootDir.add(binDir);
rootDir.add(tmpDir);
rootDir.add(usrDir);
binDir.add(new File("vi", 10000));
binDir.add(new File("latex", 20000));
rootDir.printList();
System.out.println("");
System.out.println("making usr entries......");
Directory yuki = new Directory("yuki");
Directory hanako = new Directory("hanako");
Directory tomura = new Directory("tomura");
usrDir.add(yuki);
usrDir.add(hanako);
usrDir.add(tomura);
yuki.add(new File("diary.html", 100));
hanako.add(new File("memo.txt", 200));
tomura.add(new File("game.doc", 300));
tomura.add(new File("junk.mail", 500));
rootDir.printList();
} catch (Exception e) {
e.printStackTrace();
}
}
}
making root entries......
/root(30000)
/root/bin(30000)
/root/bin/vi(10000)
/root/bin/latex(20000)
/root/tmp(0)
/root/usr(0)
making usr entries......
/root(31100)
/root/bin(30000)
/root/bin/vi(10000)
/root/bin/latex(20000)
/root/tmp(0)
/root/usr(1100)
/root/usr/yuki(100)
/root/usr/yuki/diary.html(100)
/root/usr/hanako(200)
/root/usr/hanako/memo.txt(200)
/root/usr/tomura(800)
/root/usr/tomura/game.doc(300)
/root/usr/tomura/junk.mail(500)
登场角色
- Leaf(树叶)
- 表示内容角色,不能放入其他物品。(File)
- Composite(复合物)
- 表示容器角色,可以放入容器和树叶。(Directory)
- Component
- 使leaf与Composite具有一致性,是其父类(Entity)
- client
- 使用角色,(Main)
拓展
多个和单个的一致性
Composite模式可以使容器与内容具有一致性,也可称其为,多个和单个一致性,即将多个对象结合在一起,当作一个对象下使用。
add方法
- 定义在Entity中,默认报错
- 能够使用add方法的只有Directory
- 定义在Entity中,什么也不做
- 声明但不实现
- 声明抽象方法,子类实现。(这样的话File类中也必须定义完全没有用add方法)
- 定义在Directory中