组合模式
什么是组合模式
将一组对象组织(Compose)成树形结构,以表示一种“部分 - 整体”的层次结构。组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑。
为什么要用组合模式
假如说我们现在有这样一个需求,设计一个类来表示组织机构,能方便地实现下面这些功能:
- 组织机构可以区分单位和部门,部门下不可设子部门
- 动态地添加、删除某个组织机构下的子组织机构;
- 统计指定组织机构下的组织机构个数;
我们用代码示例如下:
public class Organization {
private String name;
private Boolean isDepartment;
private List<Organization> subOrganizations = new ArrayList<>();
public Organization(String name, boolean isDepartment) {
this.name = name;
this.isDepartment = isDepartment;
}
public int countNumOfOrganizations() {
if (isDepartment) {
return 1;
}
int numOfOrganizations = 0;
for (Organization organization : subOrganizations) {
numOfOrganizations += organization.countNumOfOrganizations();
}
return numOfOrganizations;
}
public void addSubOrganization(Organization organization) {
subOrganizations.add(organization);
}
public void removeSubOrganizations(Organization organization) {
int size = subOrganizations.size();
int i = 0;
for (; i < size; ++i) {
if (subOrganizations.get(i).getName().equalsIgnoreCase(organization.getName())) {
break;
}
}
if (i < size) {
subOrganizations.remove(i);
}
}
}
单纯从功能实现角度来说,上面的代码没有问题,已经实现了我们想要的功能。但是,如果我们开发的是一个大型系统,从扩展性(单位或部门可能会对应不同的操作)、业务建模(单位和部门从业务上是两个概念)、代码的可读性(单位和部门区分对待更加符合人们对业务的认知)的角度来说,我们最好对单位和部门进行区分设计,定义为 Unity和 Department 两个类。
按照这个设计思路,我们对代码进行重构。重构之后的代码如下所示:
// 组织机构抽象类
public abstract class Organization {
private String name;
public Organization(String namet) {
this.name = name;
}
public abstract int countNumOfOrganizations();
}
// 部门具象类(不存在子节点)
public class Department extends Organization {
public Department(String namet) {
super(namet);
}
@Override
public int countNumOfOrganizations() {
return 1;
}
}
// 单位具象类(存在子节点)
public class Unity extends Organization{
private List<Organization> subOrganizations = new ArrayList<>();
public Unity(String name) {
super(name);
}
@Override
public int countNumOfOrganizations() {
int numOfOrg = 0;
for (Organization subOrganization : subOrganizations) {
numOfOrg += subOrganization.countNumOfOrganizations();
}
return numOfOrg;
}
public void addSubOrganization(Organization organization) {
subOrganizations.add(organization);
}
public void removeSubOrganizations(Organization organization) {
int size = subOrganizations.size();
int i = 0;
for (; i < size; ++i) {
if (subOrganizations.get(i).getName().equalsIgnoreCase(organization.getName())) {
break;
}
}
if (i < size) {
subOrganizations.remove(i);
}
}
}
如何使用组合模式
组合模式的用法比较固定,可以分为一下三个角色:
- 抽象父类:用来定义共有属性与方法,例如上面代码示例中的Organization;
- 具象叶子类:用来定义具象的叶子类,一般直接继承抽象父类并复写其抽象方法,例如上面代码示例中的Department;
- 具象组合类:用来定义具象的组合类,一般在继承父类的基础上增加组合相关操作,例如上面代码示例中的Unity;
总结
组合模式的设计思路,与其说是一种设计模式,倒不如说是对业务场景的一种数据结构和算法的抽象。其中,数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现。
组合模式,将一组对象组织成树形结构,将单个对象和组合对象都看做树中的节点,以统一处理逻辑,并且它利用树形结构的特点,递归地处理每个子树,依次简化代码实现。使用组合模式的前提在于,你的业务场景必须能够表示成树形结构。所以,组合模式的应用场景也比较局限,它并不是一种很常用的设计模式。