组合模式

208 阅读4分钟

目的

描述了如何将容器和叶子进行递归组合,使得用户在使用时无须对他们进行区分,可以一致的对待容器和叶子。

定义

组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构。

在组合模式中引入了抽象构件类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)能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新 的类型。