Java设计模式之——组合模式

组合模式简单介绍

组合模式也称为部分整体模式,结构型设计模式之一,组合模式比较简单,它将一组相似的对象看做一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别。生活中一个比较典型的例子就是组织结构的树状图,如下所示:

这里写图片描述

上面是一个公司的组织结构树状图,其中总公司下有行政部和研发部,而且总公司下属还有一个子公司,虽然子公司也包含行政部和研发部,但是从总公司的角度来看子公司就是一个独立的个体,与总公司所属的行政部和研发部平级。在这么一个机构中大家可以看到虽然总公司和子公司其本质不一样,但是它在我们的组织结构中是一样的,我们可以把它们看做一个抽象的公司,在组合模式中我们将这样的一个拥有分支的节点称为枝干构件,位于树状结构顶部的枝干结构比较特殊,我们称为根结构件,因为其为整个树状图的始端,同样对于向行政部和研发部这样没有分支的结构,我们则称为叶子构件,这样的一个结构就是组合模式的雏形。

组合模式的定义

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

组合模式的使用场景

  • 表示对象的部分-整体层次结构时;
  • 从一个整体中能够独立出部分模块或功能的场景

组合模式的 UML 类图

这里写图片描述

角色介绍:

  • Component:抽象根节点,为组合中的对象声明接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理 Component 的子节点。可在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它。
  • Composite:定义有子节点的那些枝干节点的行为,存储子节点,在 Component 接口中实现与子节点有关的操作。
  • Leaf:在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点对象的行为。

根据类图我们可以得出如下一个组合模式的通用模式代码:

/**
 * 抽象根节点
 */
public abstract class Component {
    protected String name;  //节点名

    public Component(String name) {
        this.name = name;
    }

    /**
     * 具体的逻辑方法由子类实现
     */
    public abstract void doSomething();

    /**
     * 添加子节点
     *
     * @param child
     */
    public abstract void addChild(Component child);

    /**
     * 移除子节点
     *
     * @param child
     */
    public abstract void removeChild(Component child);

    /**
     * 获取子节点
     *
     * @param index
     * @return
     */
    public abstract Component getChildren(int index);
}

/**
 * 具体枝干节点
 */
public class Composite extends Component {
    private List<Component> components = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        Log.d("Component", "name:" + name);
        if (null != components) {
            for (Component c : components) {
                c.doSomething();
            }
        }
    }

    @Override
    public void addChild(Component child) {
        components.add(child);
    }

    @Override
    public void removeChild(Component child) {
        components.remove(child);
    }

    @Override
    public Component getChildren(int index) {
        return components.get(index);
    }
}

/**
 * 叶子节点
 */
public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        Log.d("Component", "name:" + name);
        //叶子节点没有子节点
    }

    @Override
    public void addChild(Component child) {
        throw new UnsupportedOperationException("叶子节点没有子节点");
    }

    @Override
    public void removeChild(Component child) {
        throw new UnsupportedOperationException("叶子节点没有子节点");
    }

    @Override
    public Component getChildren(int index) {
        throw new UnsupportedOperationException("叶子节点没有子节点");
    }
}

/**
 * 客户类
 */
public class Client {
    public static void main() {
        //构造一个根节点
        Component root = new Composite("Root");

        //构造两个枝干节点
        Component branch1 = new Composite("Branch1");
        Component branch2 = new Composite("Branch2");

        //构造两个叶子节点
        Component leaf1 = new Leaf("Leaf1");
        Component leaf2 = new Leaf("Leaf2");

        //将叶子节点添加至枝干节点中
        branch1.addChild(leaf1);
        branch2.addChild(leaf2);

        //将枝干节点添加至根节点中
        root.addChild(branch1);
        root.addChild(branch2);

        //执行方法
        root.doSomething();
    }
}

可以发现组合模式中不管是叶子节点还是枝干节点都有着相同的结构,那也就意味着我们无法通过 getChildren 方法来得到具体的子节点的类型(具体原因是因为枝干节点中可以包含叶子节点或者枝干节点,不明确要返回哪种类型,所以只能返回 枝干的父类型 Component),而必须在方法实现的内部进行判断。

总结

我们平时在 Android 开发的过程中组合模式的应用并不算多,组合模式更适用于对一些界面 UI 的架构设计上,当然,绝大多数情况下,这部分代码都会由相应的程序语言提供,比如 Java 的 AWT、Android 和 IOS 的 UI 框架等,真正需要开发者去实现的不多。这里不在累赘。

组合模式的优点:

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让高层模块忽略了层次的差异,方便对整个层次结构进行控制。
  • 高层模块可以一致地使用一个组合结构或其中单个对象,不必关系处理的单个对象还是整个组合结构,简化了高层模块的代码。
  • 在组合模式中增加新的枝干构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”;
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和枝干对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

组合模式的缺点:

  • 在新增构件时不好对枝干中的构件类型进行限制,不能依赖类型系统来施加这些约束,因为在大多数情况下,它们都来自于相同的抽象层,此时,必须行时进行类型检查来实现,这个实现过程较为复杂。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章