设计模式之组合模式

定义: 也叫合成模式,将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具体有一致性。组合模式是一种结构型设计模式。
组合模式涉及的模型角色:

  • 抽象构件: 定义组合对象的共有方法和属性,规范默认接口。
  • 叶子构件: “叶子对象”,其以下没有其他分支,声明参与组合对象的行为接口。
  • 树枝构件: 参与组合对象、其下有分支的树枝对象。它的作用是将树枝和叶子组合成一个树形结构,并且定义出管理子对象的方法。

场景模拟:
一个很典型的例子就是电脑里的文件夹。在一个总文件夹里面有若干文件夹,若干文件,里面文件夹下又有文件、文件夹。整体上呈现一种树形的层级结构。像这样的数据普通写法是遍历(递归),判断是整体的话就再写一层遍历(继续递归),直到所有部分、整体都访问一遍。但是这样写在层级多的情况下会产生很多相同或类似的代码(使用递归会比遍历效果更好一些)。使用组合模式后这种情况就能得到很大的改善(虽然本质还是遍历)。下面使用组合模式来完成文件夹-文件场景的编码。
按照模型角色划分顺序编码,首先定义好抽象构建:

//抽象构件
public abstract class AbstractFile {
    /**
     * 公共属性- 文件名称
     */
    protected String name;
    /**
     * 公共方法- 显示内容
     */
    public abstract void showContent();

    AbstractFile(String name){
        this.name = name;
    }
}

叶子构件(部分)代码:

单个对象-文本类文件(叶子构件)
public class TextFile extends AbstractFile {

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

    @Override
    public void showContent() {
        System.out.println("TextFile name= "+name);
    }
}

单个对象-图片类文件(叶子构件)
public class ImageFile extends AbstractFile {
    public ImageFile(String name) {
        super(name);
    }

    @Override
    public void showContent() {
         System.out.println("ImageFile name= " + name);
        // dosomething ....
    }
}

组合构件(mini版整体)代码(树枝构件):

public class Folders extends AbstractFile {

    private ArrayList files;

    public Folders(String name) {
        super(name);
        files = new ArrayList<>();
    }

    public void addFile(AbstractFile file) {
        files.add(file);
    }

    public void deleteFile(AbstractFile file) {
        files.remove(file);
    }

    @Override
    public void showContent() {
     System.out.println("文件夹 name= "+name);
        for (AbstractFile file : files) {
            file.showContent();
        }
    }
}

场景使用:

    AbstractFile file1 = new TextFile("aaa");
    AbstractFile file2 = new TextFile("bbb");
    AbstractFile file3 = new TextFile("ccc");
    AbstractFile file4 = new TextFile("ddd");
    AbstractFile image = new ImageFile("简约风格");

    Folders folder = new Folders("e");
    Folders child1 = new Folders("f");
    Folders child2 = new Folders("g");
    folder.addFile(file1);
    folder.addFile(file2);
    folder.addFile(image);
    //
    child1.addFile(file3);
    folder.addFile(child1);
    //
    child2.addFile(file4);
    folder.addFile(child2);

    file3.showContent();
    file4.showContent();
    folder.showContent();

输出结果:
设计模式之组合模式_第1张图片
从使用外表形式上看,对文件-文件夹这么一个对象的遍历就仅仅是调用一个方法。使用组合模式后更加直接的感受是无论我是file(部分)还是Folder(整体),我对它们的使用方式是没有区别的。或者说不用去处理我当前的对象是file还是folder。兼容性极高。
应用场景:

  • 需要表示一个对象整体与部分的层次结构;
  • 对单个对象和组合对象的使用要求具有一致性的时候;

应用场景:

  • 需要表示一个对象整体与部分的层次结构;
  • 对单个对象和组合对象的使用要求具有一致性的时候(方便、快捷);

优点:

  • 可以统一地处理单个对象或组合对象,而不需要关心自己处理的是单个对象,还是组合对象,简化代码;
  • 往组合体内添加新的对象类型时不会对当前代码产生影响,兼容性高;

缺点:

  • 不容易控制树枝结构的类型。相对来说,兼容性高是优点也是缺点。
  • 不容易使用继承方式来增加新的行为。构建组合关系的时候已经将通用属性行为定义好,由子类继承才使得部分-整体拥有一致性,对于特性就很难再使用继承方式增加了。
  • 设计复杂。考虑到部分-整体的一致性关系,要花费更多时间整理层次关系,甚至额外的特性。

小结
组合模式的终极目的就是让整体-部分具有一致性,对其可以使用同样操作(参考上面提到的例子)。当整体-部分具有了一致性的时候可以简化很多步骤。例如商城类应用一般都有一个购物车。最终结算时要计算一个总价。在设计上可以将购物车-商品当做一个整体-部分来设计。计算总价时直接使用抽象构件的获取价格的方法即可。

你可能感兴趣的:(设计模式)