《Head-First-设计模式》笔记13-组合模式

  1. 定义
  2. 类图
  3. 实例
    1. 定义 Directory 基类
    2. MyFile 类
    3. Folder 类
    4. 测试
    5. 关于是否抛出异常
  4. 总结

定义

组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能够让客户以一致的方式处理个别对象以及对象组合。

使用组合结构,我们能把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。

类图

  • Component:为组合中的对象声明接口。
  • Leaf:在组合中表示没有子节点的叶节点。
  • Composite:定义有枝节点行为,用来存储子部件,在 Component 接口中实现与子部件有关操作,如增加(add)和删除(remove)等。

组合模式

实例

文件系统,文件系统里有文件和文件夹,文件夹里又有文件夹和文件...

定义 Directory 基类

所有组件必须实现 Directory 接口,然而叶节点和组合节点的角色不同,所有有些方法并不适用。面对这种情况,有时候最好是抛出运行时异常。

public abstract class Directory {
    String name;
    String description;

    /**
     * 添加指定文件/文件夹到该目录下
     * @param dir 将要添加的文件/文件夹
     * @return 添加成功/失败
     */
    public boolean add(Directory dir){
        throw new UnsupportedOperationException();//默认抛出操作异常
    }

    /**
     * 删除该目录下的指定文件/文件夹
     * @param dir 将要删除的文件/文件夹
     * @return 删除成功/失败
     */
    public boolean remove(Directory dir){
        throw new UnsupportedOperationException();//默认抛出操作异常
    }

    /**
     * 清空该目录下所有文件和文件夹
     * @return 清空成功/失败
     */
    public boolean clear(){
        throw new UnsupportedOperationException();//默认抛出操作异常
    }

    public abstract void print();

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String toString(){
        return name + description;
    }
}

MyFile 类

由于基类中对 MyFile 不支持的操作都做了默认实现(抛出异常),所以 MyFile 变得相当苗条。

public class MyFile extends Directory{
    public MyFile(String name, String desc) {
        this.name = name;
        this.description = desc;
    }

    @Override
    public void print() {
        System.out.print(this.toString());//输出文件自身信息
    }
}

Folder 类

public class Folder extends Directory{
    ArrayList<Directory> files;
    public Folder(String name, String desc){
        this.name = name;
        this.description = desc;
        this.files = new ArrayList<Directory>();
    }

    @Override
    public void print() {
        //打印该Folder自身信息
        System.out.print(this.toString() + "(");
        //打印该目录下所有文件及子文件
//        for(Directory dir : files){
//            dir.print();
//            System.out.print(", ");
//        }
        Iterator<Directory> iter = getFiles().iterator();
        while(iter.hasNext()){
            Directory dir = iter.next();
            dir.print();
            if(iter.hasNext()){
                System.out.print(",");
            }
        }

        System.out.print(")");
    }


    @Override
    public boolean add(Directory dir){
        if(files.add(dir))
            return true;
        else
            return false;
    }

    @Override
    public boolean remove(Directory dir){
        if(files.remove(dir))
            return true;
        else
            return false;
    }

    @Override
    public boolean clear(){
        files.clear();
        return true;
    }

    public ArrayList<Directory> getFiles() {
        return files;
    }
}

测试

public class Test {
    public static void main(String[] args){
        /*构造文件树*/
        /*
        C
            a.txt
            b.txt
            system
                sys.dat
            windows
                win32
                    settings
                    log.txt
                win32.config
        */
        Directory dir = new Folder("C", "");
        dir.add(new MyFile("a.txt", ""));
        dir.add(new MyFile("b.txt", ""));
        Directory subDir = new Folder("system", "");
        subDir.add(new MyFile("sys.dat", ""));
        dir.add(subDir);
        Directory subDir2 = new Folder("windows", "");
        Directory subDir3 = new Folder("win32", "");
        subDir3.add(new Folder("settings", ""));
        subDir3.add(new MyFile("log.txt", ""));
        subDir2.add(subDir3);
        subDir2.add(new MyFile("win32.config", ""));
        dir.add(subDir2);

        dir.print();//打印输出文件树
    }
}/* output:
C(a.txt, b.txt, system(sys.dat, ), windows(win32(settings(), log.txt, ), win32.config, ), )
*/

关于是否抛出异常

有些时候我们可以选择返回 null,返回 false,返回错误码等等而不是抛出异常,这些方式或许更和谐一些,但抛出异常在有些时候是对事实最贴切的表达。

举个例子,假设我们的 Folder 类有一个 add 方法,表示是否存在与之关联的应用程序,而 MyFile 不支持 add 方法,同时该属性又是从父类继承得到的,我们无法删除它。

此时我们可以选择返回 false 或者抛出异常:

返回 false:表示 MyFile 没有与之关联的应用程序
抛出异常:表示 MyFile 不支持该操作
显然,抛出异常的含义才是我们真正想要表达的

总结

  1. 组合模式提供一个结构,可同时包容个别对象和组合对象。
  2. 组合模式允许客户对个别对象以及组合对象一视同仁。
  3. 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点。
  4. 在实现组合模式时,有许多设计上的折中。你要根据需要平衡透明性和安全性。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com

文章标题:《Head-First-设计模式》笔记13-组合模式

文章字数:1.1k

本文作者:Bin

发布时间:2018-07-15, 10:17:34

最后更新:2019-08-06, 00:07:35

原始链接:http://coolview.github.io/2018/07/15/Head-First-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E3%80%8AHead-First-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8B%E7%AC%94%E8%AE%B013-%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录