1、问题
组合模式是为了在某些业务场景下无区别对待容器类和单个类。
它的常见场景如下:
1. 容器类与单个类之间无任何的关系。例如List<User>与User类,List类也可以存放任何其他类型
2. 容器类与单个类之间存在紧密的关系,容器类只能存放当前类型。例如QQ群与QQ用户之间的关系,文件目录与文件的关系
3. 容器类与单个类之间存在嵌套关系。例如房屋与厨房的关系,面板与面板中按钮的关系。
它的解决方案是:
1. 提供额外的组合类,它与原类拥有相同的父类,它的属性中存在单个类的集合,例如CompositeUser对象中存在List<User>属性。
2. 提供额外的接口类,它用于抽象业务功能,若不支持这个功能,抛出UnsupportException。
2、UML图

3、代码
ISomeOperation,它用于抽象业务的功能。
/**
*
* @File Name: IOperationInterface.java
* @Description: 它用于抽象业务功能。
* @version 1.0
* @since JDK 1.8
*/
public interface ISomeOperation {
// 添加用户
boolean add(User user) throws Exception;
// 删除用户
boolean remove(User user) throws Exception;
// 显示详细信息
String showDetail() throws Exception;
// 禁用用户
boolean disable();
}
User对象,根据具体的业务场景添加其属性,理解组合模式与User对象有多少属性无关。
/**
*
* @File Name: User.java
* @Description: 用户类实现ISomeOperation
* @version 1.0
* @since JDK 1.8
*/
public class User implements ISomeOperation {
// 姓名
private String name;
// 是否禁用
private Boolean isDisabled;
// get & set,略
// 如果是不支持的操作,直接抛出UnsupportedOperationException
@Override
public boolean add(User role) throws Exception {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(User user) throws Exception {
throw new UnsupportedOperationException();
}
// 显示用户的详细信息
@Override
public String showDetail() throws Exception {
return this.toString();
}
// 禁用用户,实际项目中更新User表
@Override
public boolean disable() throws Exception {
// 禁用disable属性
this.isDisabled = true;
return true;
}
}
Group对象,它与User类实现相同的接口,并存在User集合属性,这里设置为List<User>,其他类型也可以,例如Set<User>,User[]等。
/**
*
* @File Name: Group.java
* @Description: User类的容器类
* @version 1.0
* @since JDK 1.8
*/
public class Group implements ISomeOperation {
// User集合
List<User> users;
// get & setter方法
@Override
public boolean add(User user) throws Exception {
this.users.add(user);
return true;
}
@Override
public boolean remove(User user) throws Exception {
this.users.remove(user);
return false;
}
@Override
public String showDetail() throws Exception {
StringBuilder builder = new StringBuilder();
for (User currentUser : users) {
builder.append(currentUser.toString());
builder.append(System.getProperty("line.separator"));
}
return builder.toString();
}
@Override
public boolean disable() throws Exception {
for (User currentUser : users) {
currentUser.disable();
}
return true;
}
}
4、讨论
问题1:组合模式的主要功能是?
答:组合模式是为了在某些特殊的业务场景中无区别对待其容器类和单个类。例如停用QQ群和QQ账号的功能。
问题2:组合模式的关键点有哪些?
答:组合模式中有三个关键点。
- 容器类和元素类之间的关系。包含关系,例如数组与数组中的元素。整体-部分的关系,例如UI框架中的Panel与其包含的button之间,房屋与厨房之间的关系。划分的关系,例如省份可以拥有多个市,市可以有多个乡镇。
- 在某些业务场景下,容器类中除了添加元素类,删除元素类,查询元素类等功能之外,与单个类的功能是相同的。程序上体现为容器类与单个类的方法基本相同或实现相同的接口。
- 容器类与元素类的抽象以及功能的抽象。容器类与元素类可以抽象为同一个类型,例如文件系统的目录和文件都抽象为File类。也可以抽象为不同的类型,例如用户群组抽象为Group,用户抽象为User。
- 当抽象为同一个类型时,此时的功能可以整合到这个类中,不必在提供额外的接口。在功能实现时,需要考虑容器类和单个类的区别,例如某些操作容器类可行,而单个类不可行。
- 当抽象为不同类型时,此时需要提供额外的接口,接口的功能是容器类和单个类之间功能的交集。
5、示例
当抽象为同一个类型时,可以参考IO中的File类。此时容器类与单个类之间的关系为不同层次结构的划分。
当抽象为不同类型时,可以参考HTML5API中XXElement。此时父类与子类之间的关系为嵌套关系,最典型的就是DOM结构树,它更偏向于设计完整的类体系结构,而非组合模式的应用