版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ddxxii/article/details/84112256
介绍
本模式为结构型设计模式之一,也称为部分与整体模式。在上面说到解释器模式,里面有叶子和枝干到概念。本模式也有叶子和枝干到概念。将一组相似到对象看作一个对象处理,并根据一个树状架构来组合对象,提供一个统一的方法去访问相应的对象。总公司有子公司和其部门组成,子公司又有自己的部门,这里母公司就是根,母公司部门就是叶子,子公司就是枝干,子公司部门又是叶子。
比如:像我们文件和目录使用的则是组合模式。Android中View和ViewGroup则也是使用的组合模式。往下看下就明白了,组合模式这里谈到了两个概念:
- 透明模式: 组合使用的方法都定义在抽象类
- 安全模式: 组合使用的方法定义在自己内部
UML
透明模式
安全模式
- Component : 抽象根节点,为组合对象中的对象声明接口。在适当情况下可实现所有公共接口行为,用于访问和管理Component字节点,可在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它
- Composite: 定义字节点的那些枝干节点行为,在组合中定义对象的行为与字节点有关的操作
- Leaf: 在组合中代表叶子节点对象,叶子节点没有子节点
- Client: 通过Component直接操作对象
安全模式中,由于枝干部分行为定义在枝干中,那么只有直接使用枝干来操作,这样违背了依赖导致原则,所有有了透明模式。透明模式是都用抽象对象来操作了,但是叶子节点多了自己不需要的方法。看情况自己指定
事例
就拿文件和目录来距离把,目录下面可以放文件也可以放目录,在访问的时候,就是一个树形结构,来咱们用组合模式撸一下。
咱们的目录结构如下:
- 创建抽象根节点:
/**
* 抽象的根节点
*/
public abstract class AbsComposite {
/**
* 名称
*/
protected String name;
/**
* 处理事
*/
abstract void doSomeThis();
/**
* 是否是目录
*
* @return
*/
abstract boolean isDictionary();
}
根节点提供处理方法和名称,是否是目录方法
- 创建文件节点:
/**
* 文件实现,叶子节点
* 1. 实现抽象方法:输出当前文件名字
*/
public class FileComposite extends AbsComposite {
public FileComposite(String name) {
this.name = name;
}
@Override
void doSomeThis() {
//文件的话就直接输出名字
System.out.println("文件:" + name + ",是不是目录:" + isDictionary());
}
@Override
boolean isDictionary() {
//返回不是目录
return false;
}
}
文件节点,作为叶子节点,就只需要实现抽象方法即可
- 创建枝干节点:
/**
* 目录,枝干节点
* 1. 提供内部方法:添加、删除、获取
* 2. 实现抽象放啊放: 返回是目录,遍历当前目录下节点
*/
public class DircComposite extends AbsComposite {
/**
* 目录下装的其他节点
*/
private List<AbsComposite> absComposites = new ArrayList<>();
public DircComposite(String name) {
this.name = name;
}
@Override
void doSomeThis() {
//先输出当前目录名字
System.out.println("目录:" + name + ",是不是目录:" + isDictionary());
//遍历当前目录下的节点
for (AbsComposite absComposite : absComposites) {
absComposite.doSomeThis();
}
}
@Override
boolean isDictionary() {
//返回是目录
return true;
}
/**
* 枝干内部方法,添加节点
*
* @param absComposite 节点
*/
public void addComposite(AbsComposite absComposite) {
absComposites.add(absComposite);
}
/**
* 移除节点
*
* @param absComposite 节点
*/
public void removeComposite(AbsComposite absComposite) {
absComposites.remove(absComposite);
}
/**
* 获取子节点列表
*
* @return 子节点列表
*/
public List<AbsComposite> getChilds() {
return absComposites;
}
}
目录,枝干节点,除了实现抽象方法还需要有内部方法。
- 测试类:
//先创建要用的目录和文件
DircComposite dicTop = new DircComposite("目录1");
DircComposite dicSecond = new DircComposite("目录1中的目录2");
DircComposite dicSecond1 = new DircComposite("目录2中的目录3");
AbsComposite fileTop1 = new FileComposite("目录1中的文件1");
AbsComposite fileTop2 = new FileComposite("目录1中的文件2");
AbsComposite fileSecond1 = new FileComposite("目录2中的文件1");
AbsComposite fileSecond2 = new FileComposite("目录2中的文件2");
//将文件1和文件2放到目录1中
dicTop.addComposite(fileTop1);
dicTop.addComposite(fileTop2);
//将目录2中文件1和目录2中文件2放到目录2中
dicSecond.addComposite(fileSecond1);
dicSecond.addComposite(fileSecond2);
//目录3放到目录2中
dicSecond.addComposite(dicSecond1);
//将目录2放到目录1中
dicTop.addComposite(dicSecond);
//从顶层目录开始操作
dicTop.doSomeThis();
- 输出:
目录:目录1,是不是目录:true
文件:目录1中的文件1,是不是目录:false
文件:目录1中的文件2,是不是目录:false
目录:目录1中的目录2,是不是目录:true
文件:目录2中的文件1,是不是目录:false
文件:目录2中的文件2,是不是目录:false
目录:目录2中的目录3,是不是目录:true
我想各位从上面能看出,这里用到的是安全组合模式,组合使用的内部方法是在枝干内部,没有定义到抽象类,当然造成的情况就是测试对象那里,只能显示的声明为DircComposite了。带来的好处就是对叶子的侵入小。
使用场景
- 表示对象-部分整体层次结构时
- 从一个整体中能够独立出部分模块或功能的场景
优缺点
优点:
- 清除定义对象层次,高层模块忽略层次差异,方便对整个层次结构进行控制。
- 高层模块可以一致性使用结构中对对象(迭代)。简化了高层模块代码。
- 增加新对枝干或者叶子都比较方便,符合“开关闭原则”。
- 为树形结构提供了灵活方案。
缺点
- 牺牲了类的单一原则。
- 新增构建不好对枝干中的构件类型进行限制,不能依赖类型系统来施加这些约束,因为一般都来自相同的抽象层,就需要进行类型检查来实现。
总结:其思想是将对象组合成树形结构,使得单个对象和组合对象的使用具有一致性,部分与整体的结构就出来了,通过继承方式,可以组合兼容,方便遍历等。