定义:允许你将对象组合成树形结构来表现"整体-部分"层次结构。 组合能让客户以一致的方法处理个别对象以及组合对象。
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
英文:Composite
类型:结构型
(引)类图:
组成:
● Component(抽象构件):接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的缺省声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
● Composite(容器构件):容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
● Leaf(叶子构件):叶子节点对象,定义和实现叶子对象的行为
,不再包含其它的子节点对象。
PS: 组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。
/**
* 抽象构件角色: 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
*/
public abstract class Component {
/**
* 向组合对象中加入组件对象
* @param child 被加入组合对象中的组件对象
*/
public void add(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,
//或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 从组合对象中移出某个组件对象
* @param child 被移出的组件对象
*/
public void remove(Component child) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,
//或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 返回某个索引对应的组件对象
* @param index 需要获取的组件对象的索引,索引从0开始
* @return 索引对应的组件对象
*/
public Component getChild(int index) {
// 缺省的实现,抛出例外,因为叶子对象没有这个功能,
//或者子组件没有实现这个功能
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 示意方法,子组件对象可能有的功能方法
*/
public abstract void operation();
}
/**
* 容器构件:组合对象,通常需要存储子对象,定义有子部件的部件行为,并实现在Component里面定义的与子部件有关的操作
*/
public class Composite extends Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private List childComponents = null;
public void add(Component child) {
//延迟初始化
if (childComponents == null) {
childComponents = new ArrayList();
}
childComponents.add(child);
}
public void remove(Component child) {
if (childComponents != null) {
childComponents.remove(child);
}
}
public Component getChild(int index) {
if (childComponents != null){
if(index>=0 && index
我们用组合模式实现简单的组织架构:
+ 吊炸天股份有限公司
+ 北京分公司
- 研发部
- 市场部
- 省略...
+ 上海分公司
- 市场部
- 销售部
- 省略...
package com.designpattern.Composite;
/**
* 抽象构件角色
* @author Json<>
*/
public abstract class Component {
/**
* 向组合对象中加入组件对象
* @param child 被加入组合对象中的组件对象
*/
public void add(Component child) {
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 从组合对象中移出某个组件对象
* @param child 被移出的组件对象
*/
public void remove(Component child) {
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 返回某个索引对应的组件对象
* @param index 需要获取的组件对象的索引,索引从0开始
* @return 索引对应的组件对象
*/
public Component getChild(int index) {
throw new UnsupportedOperationException("对象不支持这个功能");
}
/**
* 输出组件自身的名称
*/
public abstract void outputSelf(String str);
}
package com.designpattern.Composite;
import java.util.ArrayList;
import java.util.List;
/**
* 容器构件
* @author Json<>
*/
public class Composite extends Component {
/**
* 用来存储组合对象中包含的子组件对象
*/
private List childComponents = null;
/**
* 组合对象的名字
*/
private String name = "";
/**
* 构造方法,传入组合对象的名字
* @param name 组合对象的名字
*/
public Composite(String name){
this.name = name;
}
/**
* 向组合对象中加入组件对象
* @param child 被加入组合对象中的组件对象
*/
public void add(Component child) {
//延迟初始化
if (childComponents == null) {
childComponents = new ArrayList();
}
childComponents.add(child);
}
/**
* 从组合对象中移出某个组件对象
* @param child 被移出的组件对象
*/
public void remove(Component child) {
if (childComponents != null) {
childComponents.remove(child);
}
}
/**
* 输出组合对象自身的结构
* @param str 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
@Override
public void outputSelf(String str) {
//先把自己输出去
System.out.println(str+"+ "+this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents != null){
//添加若干空格,表示向后缩进若干空格
str+=" ";
//输出当前对象的子对象了
for(Component c : childComponents){
//递归输出每个子对象
c.outputSelf(str);
}
}
}
}
package com.designpattern.Composite;
/**
* 叶子
* @author Json<>
*/
public class Leaf extends Component {
/**
* 叶子对象的名字
*/
private String name = "";
/**
* 构造方法,传入叶子对象的名字
* @param name 叶子对象的名字
*/
public Leaf(String name){
this.name = name;
}
/**
* 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
* @param str 前缀,主要是按照层级拼接的空格,实现向后缩进
*/
public void outputSelf(String str) {
System.out.println(str+"- "+name);
}
}
测试:
package com.designpattern.Composite;
/**
* 测试
* @author Json<>
*/
public class Client {
public static void main(String[] args) {
//定义所有的组合对象
Composite root = new Composite("吊炸天股份有限公司");
Composite c1 = new Composite("北京分公司");
Composite c2 = new Composite("上海分公司");
//定义所有的叶子对象
Leaf leaf1_1 = new Leaf("研发部");
Leaf leaf1_2 = new Leaf("市场部");
Leaf leaf1_3 = new Leaf("省略...");
Leaf leaf2_1 = new Leaf("市场部");
Leaf leaf2_2 = new Leaf("销售部");
Leaf leaf2_3 = new Leaf("省略...");
//按照树的结构来组合组合对象和叶子对象
root.add(c1);
c1.add(leaf1_1);
c1.add(leaf1_2);
c1.add(leaf1_3);
root.add(c2);
c2.add(leaf2_1);
c2.add(leaf2_2);
c2.add(leaf2_3);
//调用根对象的输出功能来输出整棵树
root.outputSelf("");
System.out.println("--------删除节点(上海分公司撤销了)↓↓↓----------");
root.remove(c2);
root.outputSelf("");
}
}
结果:
+ 吊炸天股份有限公司
+ 北京分公司
- 研发部
- 市场部
- 省略...
+ 上海分公司
- 市场部
- 销售部
- 省略...
--------删除节点(上海分公司撤销了)↓↓↓----------
+ 吊炸天股份有限公司
+ 北京分公司
- 研发部
- 市场部
- 省略...
PS:上面的例子很简单,只是实现了基本功能,在实际开发中可能遇到的情景,会比这个复杂,这里要明白组合模式的原理,化有形为无形,做到手中无剑,心中有剑;
优点:
1、 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
2、 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
3、 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
4、 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点:
在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
1、 在对象具有部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也简单。
2、 系统中需要处理一个树形结构。
3、 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
4、 想统一的使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能。
实际应用:
XML解析
组织结构树处理
文件系统设计
......
组合模式并不难理解,它主要解决的是单一对象和组合对象在使用方式上的一致性问题。如果对象具有明显的层次结构并且想要统一地使用它们,这就非常适合使用组合模式。在Web开发中,这种层次结构非常常见,很适合使用组合模式,达到对部分和整体使用的一致性。
PS:源码地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:原文地址 https://blog.csdn.net/Json_wangqiang/article/details/85276022