目的
描述了如何将容器和叶子进行递归组合,使得用户在使用时无须对他们进行区分,可以一致的对待容器和叶子。
定义
组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构。
在组合模式中引入了抽象构件类Component,它是所有容器和叶子类的公共父类,客户端针对Component进行编程。
角色
Component(抽象构件)
可以为接口或抽象类,为叶子构件和容器构件对象声明接口。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
public abstract class Component {
public abstract void add(Component component); // 增加成员
public abstract void delete(Component component); // 删除成员
public abstract Component getChild(int i); // 获取成员
public abstract void operation(); // 业务方法
}
Leaf(叶子构件)
叶子节点没有子节点,它实现了在抽象构件中定义的行为。
public class Leaf extends Component {
private String leafName;
public Leaf(String leafName) {
this.leafName = leafName;
}
@Override
public void add(Component component) {
System.out.println("不支持");
}
@Override
public void delete(Component component) {
System.out.println("不支持");
}
@Override
public Component getChild(int i) {
System.out.println("不支持");
return null;
}
@Override
public void operation() {
System.out.println(this.leafName);
}
}
Composite(容器构件)
容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
public class Composite extends Component {
private ArrayList<Component> list = new ArrayList<>();
private String name;
public Composite(String name) {
this.name = name;
}
@Override
public void add(Component component) {
list.add(component);
}
@Override
public void delete(Component component) {
list.remove(component);
}
@Override
public Component getChild(int i) {
return list.get(i);
}
@Override
public void operation() {
System.out.println("在"+this.name+"中:");
for (Object obj : list) {
((Component) obj).operation();
}
}
}
测试类
public class ComposeClient {
public static void main(String[] args) {
Component firstLevel, seconeLevel, oneLevelLeaf1, oneLevelLeaf2, secondLevelLeaf1, secondLevelLeaf2, secondLevelLeaf3;
firstLevel = new Composite("一级容器");
oneLevelLeaf1 = new Leaf("一级叶子1");
oneLevelLeaf2 = new Leaf("一级叶子2");
seconeLevel = new Composite("二级容器");
secondLevelLeaf1=new Leaf("二级叶子1");
secondLevelLeaf2 = new Leaf("二级叶子2");
secondLevelLeaf3 = new Leaf("二级叶子3");
firstLevel.add(seconeLevel);
firstLevel.add(oneLevelLeaf1);
firstLevel.add(oneLevelLeaf2);
seconeLevel.add(secondLevelLeaf1);
seconeLevel.add(secondLevelLeaf2);
seconeLevel.add(secondLevelLeaf3);
firstLevel.operation();
}
}
测试结果:
在一级容器中:
在二级容器中:
二级叶子1
二级叶子2
二级叶子3
一级叶子1
一级叶子2
透明组合模式
抽象构件Component中声明了所有用于管理成员对象的方法,包括add()、 remove()以及getChild()等方法,这样做的好处是确保所有的构件类都有相同的接口。在客户端 看来,叶子对象与容器对象所提供的方法是一致的,客户端可以相同地对待所有的对象。
透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的。叶子对象 不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供add()、remove()以及 getChild()等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可 能会出错(如果没有提供相应的错误处理代码)
安全组合模式
安全组合模式中,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在 Composite类中声明并实现这些方法。这种做法是安全的,因为根本不向叶子对象提供这些管 理成员对象的方法,对于叶子对象,客户端不可能调用到这些方法.
安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中 那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编 程,必须有区别地对待叶子构件和容器构件。在实际应用中,安全组合模式的使用频率也非 常高,在Java AWT中使用的组合模式就是安全组合模式。
优点
(1)层次分明
(2)一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整 个组合结构
(3)在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
(4)组合模式为树形结构的面向对象实现提供了一种灵活的解决方案。
缺点
增加新构件时很难对容器中的构件类型进行限制。必须通过在运行时 进行类型检查来实现,这个实现过程较为复杂
适用场景
(1)具有整体和部分的层次结构。
(2)系统中需要处理一个树形结构。
(3)能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新 的类型。