组合(Composite)模式的其它翻译名称也很多,比如合成模式、树模式等等。GOF给出的定义是:将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
(一)组合模式的组成机构
1) 抽象构件角色(Component):它为组合中的对象声明接口,也可以为共有接口实现缺省
行为。
2) 树叶构件角色(Leaf):在组合中表示叶节点对象——没有子节点,实现抽象构件角色声
明的接口。
3) 树枝构件角色(Composite):在组合中表示分支节点对象——有子节点,实现抽象构件
角色声明的接口;存储子部件。
组合模式的类图:
(二)安全性与透明性
组合模式中必须提供对子对象的管理方法,不然无法完成对子对象的添加删除等等操作,也就失去了灵活性和扩展性。但是管理方法是在 Component 中就声明还是在 Composite中声明呢?
一种方式是在 Component 里面声明所有的用来管理子类对象的方法,以达到Component 接口的最大化(如下图所示)。目的就是为了使客户看来在接口层次上树叶和分支没有区别——透明性。但树叶是不存在子类的,因此 Component 声明的一些方法对于树叶来说是不适用的。这样也就带来了一些安全性问题。
另一种方式就是只在 Composite 里面声明所有的用来管理子类对象的方法(如下图所示)。这样就避免了上一种方式的安全性问题,但是由于叶子和分支有不同的接口,所以又失去了透明性。
GOF认为:在这一模式中,相对于安全性,我们比较强调透明性。对于第一种方式中叶子节点内不需要的方法可以使用空处理或者异常报告的方式来解决。
(三)组合模式的示例代码
应用场景:实现公司类似组织架构的设计。
抽象构件角色 – Company
public abstract class Company {
protected String name;
public Company(String name) {
this.name = name;
}
public abstract void add(Company c);
public abstract void remove(Company c);
public abstract void display(int depth);
}
树枝构件角色 – ConcreteCompany
public class ConcreteCompany extends Company {
private List<Company> children = new ArrayList<Company> ();
public ConcreteCompany(String name) {
super(name);
}
@Override
public void add(Company c) {
children.add(c);
}
@Override
public void remove(Company c) {
children.remove(c);
}
@Override
public void display(int depth) {
String temp = "";
for (int i = 0; i < depth; i++)
temp += '-';
System.out.println(temp + name);
for (Company c : children) {
c.display (depth + 2);
}
}
}
树叶构件角色 – FinanceDepartment 、HrDepartment
//财务部门
public class FinanceDepartment extends Company {
public FinanceDepartment(String name){
super(name);
}
@Override
public void add(Company c) {
//不操作,叶子节点不需要扩充
}
@Override
public void remove(Company c) {
//不操作,叶子节点不需要移除
}
@Override
public void display(int depth) {
String temp = "";
for (int i = 0; i < depth; i++)
temp += '-';
System.out.println(temp + name);
}
}
//HR部门
public class HrDepartment extends Company {
public HrDepartment(String name){
super(name);
}
@Override
public void add(Company c) {
//不操作,叶子节点不需要扩充
}
@Override
public void remove(Company c) {
//不操作,叶子节点不需要移除
}
@Override
public void display(int depth) {
String temp = "";
for (int i = 0; i < depth; i++)
temp += '-';
System.out.println(temp + name);
}
}
测试代码:
public class Main {
public static void main(String[] args) {
ConcreteCompany concreteCompany = new ConcreteCompany ("总公司");
HrDepartment hrDepartment = new HrDepartment ("人力资源部门");
FinanceDepartment financeDepartment = new FinanceDepartment ("财务部门");
concreteCompany.add (hrDepartment);
concreteCompany.add (financeDepartment);
ConcreteCompany concreteCompany1 = new ConcreteCompany ("北京分公司");
concreteCompany1.add (hrDepartment);
concreteCompany1.add (financeDepartment);
concreteCompany.add (concreteCompany1);
concreteCompany.display (0);
}
}
output:
(四)组合模式的优缺点
组合模式有以下优点:
1 ) 使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关心
自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
2 ) 更容易在组合体内加入对象部件.客户端不必因为加入了新的对象部件而更改代码。这 一点符合开闭原则的要求,对系统的二次开发和功能扩展很有利!
组合模式的缺点:组合模式不容易限制组合中的构件。
(五)组合模式和装饰模式的区别
装饰模式的结构是按照组合模式来实现的——两者都有类似的结构图,都基于递归组合来组织可变数目的对象。但是两者的目的是截然不同的,组合(Composite)模式侧重通过递归组合构造类,使不同的对象、多重的对象可以“一视同仁”;而装饰(Decorator)模式仅仅是借递归组合来达到定义中的目的。