组合模式

组合模式

一、组合模式是什么?

Compose objects into tree structure to represent part-whole hierarchies.Composite lets client treat individual objects and compositions of objects uniformly.

组合模式(Composite Design Pattern):将一组对象组织成树形结构,以表示一种“部分 - 整体”的层次结构。

也可以这么说,组合模式就是把容器与内容看成同一种对象,并将它们组合成树状结构,创造一种递归的形式。

比如文件和目录,虽然直观上是不同的对象,但是如果使用组合模式来实现它们的话,可以将文件和目录都看成是继承于同一种条目类,因此算是属于同一类对象。

其次,目录中又可以继续放文件和目录,形成了递归,最终是一种树状的层次结构。

二、为什么要用组合模式?

组合模式使用的场景有限,对数据格式比较严格:

  • 数据必须是一种树形结构,类似文件和目录

尽管场景少,但是数据满足递归结构时,组合模式还是能发挥比较大的作用的:

  • 组合模式可以让客户端以统一的方式访问数据节点,因为它们是同一种对象

三、组合模式的结构是怎么样的?

组合模式包含的角色有以下几个:

  • 抽象组件(Component):为树叶组件和复合组件提供一致性的抽象接口,并实现它们的默认行为
  • 树叶组件(Leaf):表示内容,不能再放其他抽象组件对象,在结构中属于最小对象
  • 复合组件(Composite):表示容器,可以放入其他抽象组件对象,包括树叶组件和复合组件

组合模式结构

示例程序结构:

组合模式结构

抽象组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class Entry {

public abstract String getName();

public abstract int getSize();

public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}

public void printList() {
printList("");
}

protected abstract void printList(String prefix);

@Override
public String toString() {
return getName() + " (" + getSize() + ")";
}

}

树叶组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class File extends Entry {

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);
}
}

复合组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Directory extends Entry {

private String name;
private List<Entry> directory = new ArrayList<>();

public Directory(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

@Override
public int getSize() {
int size = 0;
for (Entry entry : directory) {
size += entry.getSize();
}
return size;
}

@Override
public Entry add(Entry entry) throws FileTreatmentException {
directory.add(entry);
return this;
}

@Override
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
for (Entry entry : directory) {
entry.printList(prefix + "/" + name);
}
}
}

客户端调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MainTest {

public static void main(String[] args) {
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 user 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));
yuki.add(new File("Composite.java", 300));
hanako.add(new File("memo.text", 400));
tomura.add(new File("game.doc", 500));
tomura.add(new File("junk.maik", 600));
rootDir.printList();
}

}

四、组合模式有什么优缺点?

优点:

  • 可以统一地对待内容组件和复合组件,使用简单
  • 添加新的组件,不会影响客户端调用,满足“开闭原则”

缺点:

  • 不容易识别组件对象的类型
作者

jiaduo

发布于

2022-01-15

更新于

2023-04-03

许可协议