不要因他人的优秀而嫉妒他人,每个人都有得到幸福的欲望和拥有幸福的权利
一、组合模式定义
定义:将对象组合成树形结构以表示 部分—整体 的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。该模式是结构型模式,又称为 部分整体 模式。
单个对象和组合对象:在该模式中组合对象又可以理解为容器对象,用于包含其他容器对象和单个对象,就像树枝一样上面可以拥有其他树枝也可以拥有树叶;而单个对象就像树叶,是被包含在容器对象中的,不能拥有自己的树枝和树叶。
使用具有一致性:在客户端使用对象的时候可以一致的处理单个对象和组合对象而无需关心自己处理的是单个对象还是组合对象。
文件系统我们都知道,在文件系统中文件就是单个对象,目录就是组合对象,我们可以在目录中添加其他的目录和文件,这样不断添加就会形成一个树形结构,这就是组合模式的一个很好的应用。
二、组合模式相关角色
角色 | 功能 |
---|---|
抽象构件(Component) | 单个对象和组合对象的共同接口,并且定义了他们的默认行为,同时在内部声明了拥有访问和管理实现了Component接口子类的方法(在安全方式和透明方式中略有不同) |
单个对象(Leaf) | 表示叶子节点对象,没有子节点 |
组合对象(Composite) | 相当于一个容器对象,可以容纳其余的组合对象和单个对象 |
注意:组合模式有两种:一种是安全方式的组合模式,另一种是透明方式的组合模式,关于这个后面会有介绍。
三、组合模式UML
四、案例
在我们电脑中都有一个文件系统,其中有目录和文件,目录包含文件和其他目录,文件中包含着存储的具体信息,使用组合模式将这个文件系统构建出来,使得用户可以在任意目录中添加一个文件或者其他目录。
五、案例分析
目录就是组合对象(Composite),文件就是单个对象(Leaf)这一点我们很容易理解,然后就是抽象构件(Component)该怎么写,内部应该包含什么方法。首先我们分析在案例中组合对象和单个对象的公共默认行为是什么,有一点很明确那就是open(),不管是目录还是文件都有open()方法,所以我们需要把该方法放置于抽象构件(Component)中,然后在抽象构件中我们还需要声明接口用于访问和管理Component的子部件。
(1)抽象构件(Component):创建一个接口Component,内部定义有如下抽象方法:open()、add(Component component)、remove(Component component)。
(2)单个对象(Leaf):创建一个文件类File,继承Component接口,并实现了所有方法(不过由于单个对象不能包含其他单个对象和组合对象所以add和remove方法主体为空)。
(3)组合对象(Composite):创建一个目录类Directory,继承Component接口,内部只有一个Component集合,实现了Component所有的方法。
六、案例实现
抽象构件:
package composite.pattern;
/**
* @Introduction 该类是抽象构建类
*/
public abstract class Component {
protected String name;
public Component(String nam) {
this.name=nam;
}
public abstract void open(int i); //打开方法
public abstract void add(Component component); //增加子类
public abstract void remove(Component component); //删除子类
}
单个对象代码:
package composite.pattern;
/**
* @Introduction 该类是单个对象类
*/
public class File extends Component{
public File(String nam) { //在子类中调用父类构造方法
super(nam);
}
/**实现父类中的方法*/
@Override
public void add(Component componet) {//由于为单个对象所以主体为空
}
@Override
public void remove(Component component) {
}
@Override
public void open(int i) {
for(int j=0;j<i;j++) {
System.out.print('-');
}
System.out.println("文件:"+name+"被打开");
}
}
组合对象代码:
package composite.pattern;
import java.util.LinkedList;
import java.util.List;
/**
* @Introduction 该类是组合对象类
*/
public class Directory extends Component{
private List<Component> list=new LinkedList<Component>(); //用于保存内部的Leaf或者Composite对象
public Directory(String nam) {
super(nam);
}
/**实现父类中的抽象方法*/
@Override
public void add(Component componet) {
list.add(componet);
}
@Override
public void remove(Component component) {
list.remove(component);
}
@Override
public void open(int i) {
for(int j=0;j<i;j++) {
System.out.print('-');
}
System.out.println("目录:"+name);
//将目录打开,显示目标内部信息
for(Component component:list) {
component.open(i+2);
}
}
}
用户界面代码:
package composite.pattern;
/**
1. @Introduction 该类是模拟用户界面
*/
public class Main {
public static void main(String[] args) {
File file1=new File("大三"); //大三文件
File file2=new File("大二"); //大二文件
Directory directory1=new Directory("根目录");
Directory directory2=new Directory("大学目录");
//将多个对象进行组合
directory1.add(directory2);
directory2.add(file2);
directory2.add(file1);
directory1.open(1);
//将file2移动到根目录下
directory2.remove(file2);
directory1.add(file2);
directory1.open(1);
//这里用户不需要分辨单个对象和组合对象
file1.add(directory2);
file2.remove(directory2);
}
}
运行结果:
七、两种组合模式
透明式组合模式:
抽象构件中声明管理和访问子对象的方法,例如add(Component component)等,这样所有继承了抽象构件的子类都会去实现该方法。这种类型的组合模式被称为透明式组合模式。在上面的案例中我们就在Component类体中声明了管理和访问子类的抽象方法,所以它是透明式的。
该种方法的优点在于Leaf对象和Composite对象中都包含有同样的方法,所以用户在使用的时候完全不用考虑自己正在用的是Leaf对象还是Composite对象。但缺点也在于此,由于Leaf对象不具这种相关方法,所以它的实现是没有意义的,使用的时候可能还会造成一些不必要的麻烦。
安全式组合模式:
在抽象构件中不去声明管理和访问子对象的方法,只有Leaf和Coposite的公有默认属性,这种类型的组合模式被称为安全式组合模式。
优点:由于将对子对象的管理和访问等方法放在Composite类体中,这样可以是用户在使用该模式的时候更加安全,不会造成在Leaf对象中增加其他Leaf对象或者Composite对象等错误的举动;缺点是由于Leaf类和Composite类中拥有不同的方法,所以用户在使用该中组合模式的时候需要进行相关判断。
八、组合模式优缺点
优点:
- 对象与对象之间可以进行多种组合,提高了类与类之间的灵活性
- 提高了系统的扩拓展性,如果我们需要增加一个新的Leaf,只需要新构建一个类,然后在客户端使用即可。
- 在客户端,Leaf对象和Composite对象可以一致的使用,使得系统的操作更加简单,从某种方面来说降低了系统复杂性。
缺点:
由于所有的Leaf和Composite都是继承了Component,所以在新增Component对象时很难对组合对象中需要管理和访问的Component类型进行限制。