对于树形结构,容器对象(如文件夹)可以进行添加删除叶子对象(如文件)等操作,但是叶子对象就不能具备这样的操作,所以容器对象和叶子对象需要区别对待,但是这样会使得程序非常复杂,通常我们希望可以一致的处理它们,组合模式就是为此类问题而诞生,组合模式通过一种巧妙的设计方案使得用户可以一致性地处理整个树形结构或者树形结构的一部分,也可以一致性地处理树形结构中的叶子对象和容器对象

组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构,组合模式又可以称为“整体-部分”(Part-Whole)模式,是一种结构型模式

透明组合模式

  • Component(抽象构件):可以是接口或者抽象类,为叶子对象和容器对象声明接口
  • Leaf(叶子对象):对于增加、删除等操作,可以通过异常进行处理,只实现属于叶子对象的方法
  • Composite(容器对象):包含叶子对象的容器

先来用透明组合模式实现一个获取文件后缀名的操作:

Component 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class AbstractFile {
private String name;

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

public abstract void addFile(AbstractFile file);
public abstract void removeFile(AbstractFile file);
public abstract AbstractFile getFile(int index);
public abstract void getFileSuffix();

}

Leaf 类:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Java 文件对象
public class JavaFile extends AbstractFile {


public JavaFile(String name) {
super(name);
}

@Override
public void addFile(AbstractFile file) {
throw new UnsupportedOperationException("不支持操作");
}

@Override
public void removeFile(AbstractFile file) {
throw new UnsupportedOperationException("不支持操作");
}


@Override
public AbstractFile getFile(int index) {
throw new UnsupportedOperationException("不支持操作");
}

@Override
public void getFileSuffix() {
LogUtils.i("Java 文件的后缀名是 .java");
}
}

// text 文件对象
public class TextFile extends AbstractFile {
public TextFile(String name) {
super(name);
}



@Override
public void addFile(AbstractFile file) {
throw new UnsupportedOperationException("不支持操作");
}

@Override
public void removeFile(AbstractFile file) {
throw new UnsupportedOperationException("不支持操作");
}


@Override
public AbstractFile getFile(int index) {
throw new UnsupportedOperationException("不支持操作");
}

@Override
public void getFileSuffix() {
LogUtils.i("Text 文件的后缀名是 .txt");
}
}

Composite 类:

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
public class Folder extends AbstractFile {

ArrayList<AbstractFile> fileList = new ArrayList<>();

public Folder(String name) {
super(name);
}

@Override
public void addFile(AbstractFile file) {
fileList.add(file);
}

@Override
public void removeFile(AbstractFile file) {
fileList.remove(file);
}

@Override
public AbstractFile getFile(int index) {
return fileList.get(index);
}

@Override
public void getFileSuffix() {
LogUtils.i("文件夹无后缀名");

for (AbstractFile abstractFile : fileList) {
abstractFile.getFileSuffix();
}
}
}

Client 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
AbstractFile text1 = new TextFile("text1");
AbstractFile text2 = new TextFile("text2");

AbstractFile javaFile1 = new JavaFile("javaFile1");
AbstractFile javaFile2 = new JavaFile("javaFile2");

AbstractFile folder1 = new Folder("folder1");
AbstractFile folder2 = new Folder("folder2");
AbstractFile folder3 = new Folder("folder3");

folder1.addFile(text1);
folder1.addFile(javaFile1);

folder2.addFile(text2);
folder2.addFile(javaFile2);

folder3.addFile(folder1);
folder3.addFile(folder2);
folder3.getFileSuffix();

如果仅仅是获取文件后缀名,暂时没有任何问题,但是不熟悉代码得小伙伴调用了 TextFile 中的 addFile() 函数,又没有进行异常处理,就会导致程序出现异常,这就是透明组合模式的缺点,对于调用者来说所有函数足够透明,但是一不小心就会导致程序出现异常

下面再来介绍另外一种组合模式,叫做安全组合模式:

安全组合模式

利用该模式再实现一下上面获取文件后缀名的操作:

Component 类:

1
2
3
4
5
6
7
8
9
10
public abstract class AbstractSafeFile {
private String name;

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

public abstract void getFileSuffix();

}

Leaf 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Java 文件对象
public class JavaFile extends AbstractSafeFile {

public JavaFile(String name) {
super(name);
}

@Override
public void getFileSuffix() {
LogUtils.i("Java 文件的后缀名是 .java");
}
}

// text 文件对象
public class TextFile extends AbstractSafeFile {
public TextFile(String name) {
super(name);
}

@Override
public void getFileSuffix() {
LogUtils.i("Text 文件的后缀名是 .txt");
}
}

Composite 类:

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
public class Folder extends AbstractSafeFile {

ArrayList<AbstractFile> fileList = new ArrayList<>();

public Folder(String name) {
super(name);
}

@Override
public void addFile(AbstractFile file) {
fileList.add(file);
}

@Override
public void removeFile(AbstractFile file) {
fileList.remove(file);
}

@Override
public AbstractFile getFile(int index) {
return fileList.get(index);
}

@Override
public void getFileSuffix() {
LogUtils.i("文件夹无后缀名");

for (AbstractFile abstractFile : fileList) {
abstractFile.getFileSuffix();
}
}
}

Client 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TextFile text1 = new TextFile("text1");
TextFile text2 = new TextFile("text2");

JavaFile javaFile1 = new JavaFile("javaFile1");
JavaFile javaFile2 = new JavaFile("javaFile2");

Folder folder1 = new Folder("folder1");
Folder folder2 = new Folder("folder2");
Folder folder3 = new Folder("folder3");

folder1.addFile(text1);
folder1.addFile(javaFile1);

folder2.addFile(text2);
folder2.addFile(javaFile2);

folder3.addFile(folder1);
folder3.addFile(folder2);
folder3.getFileSuffix();

安全组合模式中的 Component 类不提供任何管理成员对象的方法,在 Composite 类中自己实现这些方法,这种做法虽然安全了,但是不能针对抽象编程,不够透明,要有区别的对待叶子对象和容器对象,正所谓鱼与熊掌不可兼得