组合设计模式也叫部分整体设计模式,是一种结构型设计模式,它将一组相似的对象看做一个对象来处理,从而可以一视同仁的对所有对象进行统一的访问。在Java系统中,一般以树形结构来形容这种组合关系。
定义
将对象组合成树状结构以表示部分-整体的层次结构,使得外部对单个对象和对象集合的访问具有一致性。
场景
- 需要表示对象的部分-整体结构时
- 从一个整体中能够独立出部分模块或者功能的场景
角色
抽象根节点
为组合中的对象声明的接口,当然也可以实现一些缺省行为和共有逻辑
枝干节点
定义有子节点的枝干节点的行为,存储子节点,实现抽象根节点中跟子节点有关的操作
叶子节点
定义树形结构中那些没有子节点的叶子节点的行为
分类
根据抽象根节点定义的行为方法的状况,组合模式可以分为透明组合模式和安全组合模式。
透明组合模式
抽象根节点类中定义了组合对象所有的行为方法。在引用时,可以直接接口引用
安全组合模式
抽象根节点类只定义了组合对象的公用行为方法。子节点的接口由自己来定义,在引用时,需要实例类引用
代码说明
组合设计模式在实际开发中使用频率不是非常高,一般这种设计模式在软件系统中,会由系统自身来实现一些系统层面的模块或者框架。现在假设有这样一个场景,一个公司下面有多个部门,部门由具体的成员构成。当需要遍历整个公司的职员时,我们可以采用组合设计模式来递归访问,在本例中,我们采用透明组合模式来实现。
针对公司的所有职员,都可以抽象成一个接口。
/**
* 组合模式抽象根节点:公司职员抽象接口
*/
public interface IStaff {
/**
* 枝干节点获取子节点的方法
*/
IStaff getChild(int index);
/**
* 枝干节点增加子节点的方法
*/
void addChild(IStaff child);
/**
* 枝干节点移除子节点的方法
*/
void removeChild(IStaff child);
/**
* 组和对象共有的行为方法
*/
void information();
}
可以看到,透明模式是需要根节点不仅仅定义共有的行为方法,还需要定义枝干节点跟叶子节点交互的方法
接下来,需要实现枝干节点和叶子节点。
枝干节点:部门。
/**
* 枝干节点:部门
*/
public class Department implements IStaff {
/**
* 部门名称
*/
private String name;
/**
* 部门内部员工
*/
private List<IStaff> mems = new ArrayList<>();
public Department(String name) {
this.name = name;
}
@Override public IStaff getChild(int index) {
if (index < mems.size()) {
return mems.get(index);
}
return null;
}
@Override public void addChild(IStaff child) {
mems.add(child);
}
@Override public void removeChild(IStaff child) {
mems.remove(child);
}
@Override public void information() {
System.out.println("***部门 " + name + "***");
for (IStaff staff : mems
) {
staff.information();
}
}
}
叶子节点:普通员工。
/**
* 叶子节点:普通员工
*/
public class Staff implements IStaff {
private String name;
private String position;
public Staff(String name, String position) {
this.name = name;
this.position = position;
}
/**
* 叶子节点空实现
*/
@Override public IStaff getChild(int index) {
return null;
}
/**
* 叶子节点空实现
*/
@Override public void addChild(IStaff child) {
}
/**
* 叶子节点空实现
*/
@Override public void removeChild(IStaff child) {
}
@Override public void information() {
System.out.println("员工:姓名 " + name + ", 职位 " + position);
}
}
上文中,我们只实现了两级结构,实际上,一般会有多级结构。现在我们手动构造一个部门,然后来对该部门进行遍历。
public static void main(String[] args) {
// 构建组合对象树形结构,仅构建了两级
IStaff dep = new Department("技术产品部");
dep.addChild(new Staff("robin","算法工程师"));
dep.addChild(new Staff("martin","产品经理"));
dep.addChild(new Staff("jackMa","销售顾问"));
// 遍历部门
dep.information();
}
对应输出为:
***部门 技术产品部***
员工:姓名 robin, 职位 算法工程师
员工:姓名 martin, 职位 产品经理
员工:姓名 jackMa, 职位 销售顾问
关于源码
安卓系统源码中,视图体系View
就是运用了组合模式。View
是根节点,定义了所有的共有行为方法;ViewGroup
的各种实现类是枝干节点,他们具备操作子视图的addView
等方法;View
的非容器实现类是叶子节点,叶子节点不具备枝干节点操作子视图的功能。这里安卓将所有枝干节点又进行了一次抽象,使得ViewGroup
具备了容器的功能。在这种设计模式下,在对视图进行遍历时,可以一视同仁的对待每一个控件(都是View类型对象)。
总结
事实上,在应用开发中组合模式使用频率比较低。因为这种设计模式在UI架构体系中使用较多,而这种体系一般又会由操作系统自身来实现。组合模式是通过继承来实现的,在灵活性上有一定的局限性,而且枝干节点的职责有溢出,不符合类的单一职责原则。但是组合设计模式的优点也很明显,他提供了一种简单的方式来对相似的集合对象进行递归访问。