1. 定义:组合多个对象形成树形结构以表示具有”整体一部分”关系的层次结构,是一种对象结构型模式
2. 结构图
3. 组合模式中包含的角色
a) Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,包含了所有子类共有行为的声明和实现定义了访问及管理它的子构件的方法
b) Leaf(叶子构件):它在组合模式结构中表示叶子节点对象,叶子节点没有子节点,实现了在抽象构件中定义的行为
c) Composite(容器构件):容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,,提供一个集合存储子节点,实现了抽象构件中定义的行为,在其业务方法中可以传递归调其子节点的业务方法
组合模式关键是定义一个抽象构件类,它既可以代表叶子,也可以代表容器,客户端针对该抽象构件类进行编程,无需知道它是叶子还是容器,可以对其进行统一处理,容器对象与抽象构件类之间建立了一个聚合关联关系在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构
4. 核心代码:
abstract class Component {
public abstract void add(Component c); //增加成员
public abstract void remove(Component c); //修改成员
public abstract Component getChild(int i); //获取成员
public abstract void operation(); //业务方法
}
class Leaf extends Component {
public void add(Component c){
//异常处理或错误提示
}
public void remove(Component c) {
//异常处理或错误提示
}
public Component getChild(int i) {
//异常处理或错误提示
return null;
}
public void operation() {
//叶子构件具体业务方法的实现
}
}
//容器构件
class Composite extends Component {
private ArrayList<Component> list = new ArrayList<Component>();
public void add(Componet c) {
list.add(c);
}
public void remove(Component c) {
list.remove(c);
}
public Componet getChild(int i) {
return (Component) list.get(i);
}
public void operation() {
//递归调用成员构件的业务方法
for(Object obj : list) {
((Component) obj).operation();
}
}
}
5. 案例
6. 实现的代码
package com.zach.composite;
//抽象构件
public abstract class AbstractFile {
public abstract void add(AbstractFile file);
public abstract void remove(AbstractFile file);
public abstract AbstractFile getChild(int i);
public abstract void killVirus();
}
package com.zach.composite;
//叶子构件:文本类
public class TextFile extends AbstractFile {
private String name;
public TextFile(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("添加文件失败");
}
@Override
public void remove(AbstractFile file) {
System.out.println("删除文件失败");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("获取文件失败");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对文本文件"+name + "进行杀毒");
}
}
package com.zach.composite;
//叶子构件:视频类
public class VideoFile extends AbstractFile {
private String name;
public VideoFile(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("添加视频文件失败");
}
@Override
public void remove(AbstractFile file) {
System.out.println("删除视频文件失败");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("获取视频文件失败");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对视频文件"+name + "进行杀毒");
}
}
package com.zach.composite;
//图片文件叶子构件类
public class ImageFile extends AbstractFile {
private String name;
public ImageFile(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("添加图像文件出错");
}
@Override
public void remove(AbstractFile file) {
System.out.println("删除图像文件出错");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("获取图像文件出错");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对图像文件"+name + "进行杀毒");
}
}
package com.zach.composite;
import java.util.ArrayList;
//文件夹类:容器构件
public class Folder extends AbstractFile{
private ArrayList<AbstractFile> fileList = new ArrayList<>();
private String name;
public Folder(String name) {
this.name = name;
}
public ArrayList<AbstractFile> getFileList() {
return fileList;
}
public void setFileList(ArrayList<AbstractFile> fileList) {
this.fileList = fileList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
fileList.add(file);
}
@Override
public void remove(AbstractFile file) {
fileList.remove(file);
}
@Override
public AbstractFile getChild(int i) {
// TODO Auto-generated method stub
return fileList.get(i);
}
@Override
public void killVirus() {
System.out.println("****对文件夹"+name+"进行杀毒"); //模拟杀毒
//递归调用成员构件的killVirus()方法
for (AbstractFile abstractFile : fileList) {
abstractFile.killVirus();
}
}
}
package com.zach.composite;
//客户端
public class Client {
public static void main(String[] args) {
//针对抽象构件编程
AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;
folder1 = new Folder("Sunny的资料");
folder2 = new Folder("图像文件");
folder3 = new Folder("文本文件");
folder4 = new Folder("视频文件");
file1 = new ImageFile("波多野结衣.jpg");
file2 = new ImageFile("北条麻妃.gif");
file3 = new TextFile("波多野结衣.text");
file4 = new TextFile("波多野结衣.doc");
file5 = new VideoFile("东京热.av");
folder2.add(file1);
folder2.add(file2);
folder3.add(file3);
folder3.add(file3);
folder3.add(file4);
folder4.add(file5);
folder1.add(folder2);
folder1.add(folder3);
folder1.add(folder4);
//从"Sunny"的资料节点开始进行杀毒操作
folder1.killVirus();
}
}
运行结果
****对文件夹Sunny的资料进行杀毒
****对文件夹图像文件进行杀毒
----对图像文件波多野结衣.jpg进行杀毒
----对图像文件北条麻妃.gif进行杀毒
****对文件夹文本文件进行杀毒
----对文本文件波多野结衣.text进行杀毒
----对文本文件波多野结衣.text进行杀毒
----对文本文件波多野结衣.doc进行杀毒
****对文件夹视频文件进行杀毒
----对视频文件东京热.av进行杀毒
7. 透明组合模式与安全组合模式
a) 透明组合模式
缺点:
i. 不够安全,叶子对象不可能有下一个层次的对象,因此add(),remove(),getChild()是没有意义,在运行阶段调用这些方法可能出错
b) 安全组合模式
在抽象构件Component中没有声明任何用于管理成员对象的方法,而在Composite类中声明实现这些方法;在实际应用中,使用频率也很高,在Java AWT中使用的组合模式就是安全组合模式
缺点:
i. 不够透明,叶子构件和容器构件具有不同的方法,且容器构件中用于管理成员对象的方法没有在抽象构件类中定义,故客户端不能完全针对抽象构件编程
8. Sunny公司的组织结构
a) 公司的内部办公系统(OA系统)中,有一个与公司组织结构对应的树形菜单,行政人员可以给各级单位下发通知,这些单位可以是总公司的一个部门,也可以是一个分公司,或分公司的一个部分,用户只需选择一个根节点即可实现通知的下发操作,无须关心具体的实现细节
b) 公司组合模式的结构图
"单位"充当了抽象构件角色,"公司"充当了容器构件角色,"研发部","财务部"和"人力资源部"充当了叶子构件角色
9. 组合模式的总结
组合模式使用面向对象的思想来实现树形结构的构建与处理,描述了如何将容器对象和叶子对象进行递归组合,实现灵活简单,在JavaSE中的AWT和Swing包的设计就基于组合模式,在这些界面包中提供了大量容器构件(如Container)和成员构件(如:Checkbox,Button和TextComponent等)
a) 主要优点
a. 清楚的定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制
b. 客户端可以一致的使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构
b) 主要缺点
增加新构件时很难对容器中构件类型进行限制,如有时希望一个容器中只能有某些特定类型的对象,在某个文件夹中只能包含文本文件,使用组合模式时不能依赖系统来施加约束,因为它们来自同一个抽象层,在这种情况下,必须通过运行时进行类型检查来实现
10. 适用场景
a) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致性地对待它们
b) 在一个使用面向对象语言开发的系统中需要处理一个树形结构
c) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型
2. 结构图
3. 组合模式中包含的角色
a) Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,包含了所有子类共有行为的声明和实现定义了访问及管理它的子构件的方法
b) Leaf(叶子构件):它在组合模式结构中表示叶子节点对象,叶子节点没有子节点,实现了在抽象构件中定义的行为
c) Composite(容器构件):容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,,提供一个集合存储子节点,实现了抽象构件中定义的行为,在其业务方法中可以传递归调其子节点的业务方法
组合模式关键是定义一个抽象构件类,它既可以代表叶子,也可以代表容器,客户端针对该抽象构件类进行编程,无需知道它是叶子还是容器,可以对其进行统一处理,容器对象与抽象构件类之间建立了一个聚合关联关系在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构
4. 核心代码:
abstract class Component {
public abstract void add(Component c); //增加成员
public abstract void remove(Component c); //修改成员
public abstract Component getChild(int i); //获取成员
public abstract void operation(); //业务方法
}
class Leaf extends Component {
public void add(Component c){
//异常处理或错误提示
}
public void remove(Component c) {
//异常处理或错误提示
}
public Component getChild(int i) {
//异常处理或错误提示
return null;
}
public void operation() {
//叶子构件具体业务方法的实现
}
}
//容器构件
class Composite extends Component {
private ArrayList<Component> list = new ArrayList<Component>();
public void add(Componet c) {
list.add(c);
}
public void remove(Component c) {
list.remove(c);
}
public Componet getChild(int i) {
return (Component) list.get(i);
}
public void operation() {
//递归调用成员构件的业务方法
for(Object obj : list) {
((Component) obj).operation();
}
}
}
5. 案例
实现方案
6. 实现的代码
package com.zach.composite;
//抽象构件
public abstract class AbstractFile {
public abstract void add(AbstractFile file);
public abstract void remove(AbstractFile file);
public abstract AbstractFile getChild(int i);
public abstract void killVirus();
}
package com.zach.composite;
//叶子构件:文本类
public class TextFile extends AbstractFile {
private String name;
public TextFile(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("添加文件失败");
}
@Override
public void remove(AbstractFile file) {
System.out.println("删除文件失败");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("获取文件失败");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对文本文件"+name + "进行杀毒");
}
}
package com.zach.composite;
//叶子构件:视频类
public class VideoFile extends AbstractFile {
private String name;
public VideoFile(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("添加视频文件失败");
}
@Override
public void remove(AbstractFile file) {
System.out.println("删除视频文件失败");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("获取视频文件失败");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对视频文件"+name + "进行杀毒");
}
}
package com.zach.composite;
//图片文件叶子构件类
public class ImageFile extends AbstractFile {
private String name;
public ImageFile(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("添加图像文件出错");
}
@Override
public void remove(AbstractFile file) {
System.out.println("删除图像文件出错");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("获取图像文件出错");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对图像文件"+name + "进行杀毒");
}
}
package com.zach.composite;
import java.util.ArrayList;
//文件夹类:容器构件
public class Folder extends AbstractFile{
private ArrayList<AbstractFile> fileList = new ArrayList<>();
private String name;
public Folder(String name) {
this.name = name;
}
public ArrayList<AbstractFile> getFileList() {
return fileList;
}
public void setFileList(ArrayList<AbstractFile> fileList) {
this.fileList = fileList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
fileList.add(file);
}
@Override
public void remove(AbstractFile file) {
fileList.remove(file);
}
@Override
public AbstractFile getChild(int i) {
// TODO Auto-generated method stub
return fileList.get(i);
}
@Override
public void killVirus() {
System.out.println("****对文件夹"+name+"进行杀毒"); //模拟杀毒
//递归调用成员构件的killVirus()方法
for (AbstractFile abstractFile : fileList) {
abstractFile.killVirus();
}
}
}
package com.zach.composite;
//客户端
public class Client {
public static void main(String[] args) {
//针对抽象构件编程
AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;
folder1 = new Folder("Sunny的资料");
folder2 = new Folder("图像文件");
folder3 = new Folder("文本文件");
folder4 = new Folder("视频文件");
file1 = new ImageFile("波多野结衣.jpg");
file2 = new ImageFile("北条麻妃.gif");
file3 = new TextFile("波多野结衣.text");
file4 = new TextFile("波多野结衣.doc");
file5 = new VideoFile("东京热.av");
folder2.add(file1);
folder2.add(file2);
folder3.add(file3);
folder3.add(file3);
folder3.add(file4);
folder4.add(file5);
folder1.add(folder2);
folder1.add(folder3);
folder1.add(folder4);
//从"Sunny"的资料节点开始进行杀毒操作
folder1.killVirus();
}
}
运行结果
****对文件夹Sunny的资料进行杀毒
****对文件夹图像文件进行杀毒
----对图像文件波多野结衣.jpg进行杀毒
----对图像文件北条麻妃.gif进行杀毒
****对文件夹文本文件进行杀毒
----对文本文件波多野结衣.text进行杀毒
----对文本文件波多野结衣.text进行杀毒
----对文本文件波多野结衣.doc进行杀毒
****对文件夹视频文件进行杀毒
----对视频文件东京热.av进行杀毒
7. 透明组合模式与安全组合模式
a) 透明组合模式
透明组合模式中,抽象构件Component中声明了所有用于管理成员对象的方法,确保了所有的构件类都有相同的接口,从客户端看,叶子对象与容器对象所提供的方法一致,客户端可以相同地对待所有的对象;透明组合模式是组合模式的标准形式
缺点:
i. 不够安全,叶子对象不可能有下一个层次的对象,因此add(),remove(),getChild()是没有意义,在运行阶段调用这些方法可能出错
b) 安全组合模式
在抽象构件Component中没有声明任何用于管理成员对象的方法,而在Composite类中声明实现这些方法;在实际应用中,使用频率也很高,在Java AWT中使用的组合模式就是安全组合模式
缺点:
i. 不够透明,叶子构件和容器构件具有不同的方法,且容器构件中用于管理成员对象的方法没有在抽象构件类中定义,故客户端不能完全针对抽象构件编程
8. Sunny公司的组织结构
a) 公司的内部办公系统(OA系统)中,有一个与公司组织结构对应的树形菜单,行政人员可以给各级单位下发通知,这些单位可以是总公司的一个部门,也可以是一个分公司,或分公司的一个部分,用户只需选择一个根节点即可实现通知的下发操作,无须关心具体的实现细节
b) 公司组合模式的结构图
"单位"充当了抽象构件角色,"公司"充当了容器构件角色,"研发部","财务部"和"人力资源部"充当了叶子构件角色
9. 组合模式的总结
组合模式使用面向对象的思想来实现树形结构的构建与处理,描述了如何将容器对象和叶子对象进行递归组合,实现灵活简单,在JavaSE中的AWT和Swing包的设计就基于组合模式,在这些界面包中提供了大量容器构件(如Container)和成员构件(如:Checkbox,Button和TextComponent等)
a) 主要优点
a. 清楚的定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制
b. 客户端可以一致的使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构
b) 主要缺点
增加新构件时很难对容器中构件类型进行限制,如有时希望一个容器中只能有某些特定类型的对象,在某个文件夹中只能包含文本文件,使用组合模式时不能依赖系统来施加约束,因为它们来自同一个抽象层,在这种情况下,必须通过运行时进行类型检查来实现
10. 适用场景
a) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致性地对待它们
b) 在一个使用面向对象语言开发的系统中需要处理一个树形结构
c) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型