设计模式之复合模式

DesignPattern之复合模式

1. Definition

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



2. 分析(Head First Design Pattern p358)

根据 Head First Design Pattern 第358页的uml图来分析:

  • 类图当中有三个类,一个是Component(节点的统一接口),它的目的是为了统一节点的操作。

  • 接下来的两个实现类,一个则是非叶子节点(Composite),它可以有子节点,子节点可以是Leaf,也可以是Composite。

  • 另外一个则是叶子节点(Leaf),它不能含有子节点。

  • 客户操作的Component,也就是说,无论是Leaf还是Composite,在客户看来其实都是Component。那如何让客户将Composite和Leaf一视同仁呢?很简单,只要让Component中同时包含Leaf和Composite的方法就可以了。

  • 那现在问题又来了,有些方法是Leaf或者Composite独有的,但是Leaf或者Composite都实现了Component接口,必须实现全部方法,然而让Leaf实现Composite独有的方法并没有什么意义。如何解决这个问题呢?——> 解决的方案有好多种,说一种书上给出的方案:让这些方法抛出UnsupportedOperationException就可以了。



3. 例子

就拿电脑的文件系统来举例子:文件夹是一个文件,单个的文件,比如“.avi”, ”.txt”也是文件,所以可以让它们都实现一个MyFile接口。它们之间的共性有好多,比如都可以进行删除操作。但是删除文件跟删除文件夹是有区别的:

  • 删除文件:只需要删除当前文件就可以了
  • 删除文件夹:必须要删除这个文件夹里面的所有文件和文件夹

那么组合模式的一致性(客户能一视同仁的地方)就体现在: 客户不需要知道当前操作的是单个文件还是文件夹,客户只知道要进行删除文件(这里的文件指的是接口MyFile)操作就可以了

  • 接口 MyFile

~~~java

package composite;

public interface MyFile {


    public void delete();

    public String getName();

    public void createNewFile(String name);

    // Only for folder
    public void deleteFile(String name);

    // only for folder
    public MyFile getMyFile(int index);

    public void show();
}

~~~

  • 文件夹 Folder:

Folder中有一个集合,用来保存文件夹里面的单个文件及子文件夹

~~~java

package composite;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Folder implements MyFile {

    private String name;
    private MyFile folder;
    private List<MyFile> files;

    public Folder(String name) {
        this(name, null);
    }

    public Folder(String name, MyFile folder) {
        this.name = name;
        this.folder = folder;
        this.files = new ArrayList<MyFile>();
    }

    @Override
    public void delete() {

        System.out.println("delete subfiles...\n");

        for (Iterator<MyFile> i = this.files.iterator(); i.hasNext();) {
            i.next();
            i.remove();
        }

        System.out.println("finished deleting subfiles!\n");

        System.out.println("---Deleted [" + this.name + "]---\n");
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void createNewFile(String name) {
        if (name.contains(".")) {
            files.add(new File(name, this));
        } else {
            files.add(new Folder(name, this));
        }
    }

    @Override
    public void deleteFile(String name) {
        for (MyFile file : this.files) {
            if (file.getName().equals(name)) {
                this.files.remove(file);
                System.out.println("---Deleted [" + file.getName() + "]---\n");
                break;
            }
        }
    }

    @Override
    public MyFile getMyFile(int index) {
        return this.files.get(index);
    }

    @Override
    public void show() {
        System.out.println("------" + this.name + "------");
        for (MyFile file : this.files) {
            file.show();
        }
    }

}

~~~

  • 单个文件 File:

~~~java

package composite;

public class File implements MyFile {

    private String name;
    private MyFile folder;

    public File(String name, MyFile folder) {
        this.name = name;
        this.folder = folder;
    }

    @Override
    public void delete() {
        this.folder.deleteFile(this.name);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void createNewFile(String name) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void deleteFile(String name) {
        throw new UnsupportedOperationException();
    }

    @Override
    public MyFile getMyFile(int index) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void show() {
        System.out.println("[" + this.name + "]");
    }

}

~~~

  • 测试类:

~~~java

~~~package composite;

public class Demo {

    public static void main(String[] args) {

        //-----------root-------------------
        MyFile root = new Folder("My Computer");

        root.createNewFile("Disk C");
        root.createNewFile("Disk D");
        root.createNewFile("Disk E");


        //---------Disk D--------------------
        MyFile diskD = root.getMyFile(1);

        diskD.createNewFile("Project");
        diskD.createNewFile("Movies");

        //--------project--------------------
        MyFile project = diskD.getMyFile(0);
        project.createNewFile("Test1.java");
        project.createNewFile("Test2.java");
        project.createNewFile("Test3.java");

        //--------movies--------------------
        MyFile movies = diskD.getMyFile(1);
        movies.createNewFile("Matrix1.avi");
        movies.createNewFile("Matrix2.avi");

        // print all files
        System.out.println("Before deletion:");
        root.show();

        // Now try to delete some files!
        System.out.println();
        project.delete();
        movies.deleteFile("Matrix2.avi");

        System.out.println("After deletion:");
        root.show();



    }

}

~~~

  • 结果:

~~~java

Before deletion:
------My Computer------

------Disk C------

------Disk D------

------Project------

-[Test1.java]

-[Test2.java]

-[Test3.java]

------Movies------

-[Matrix1.avi]

-[Matrix2.avi]

------Disk E------


delete subfiles...

finished deleting subfiles!

---Deleted [Project]---

---Deleted [Matrix2.avi]---

After deletion:
------My Computer------

------Disk C------

------Disk D------

------Project------

------Movies------

-[Matrix1.avi]

------Disk E------


~~~

4. 思考

组合模式以牺牲单一责任原则来换取透明性(transparency)。透明性指:通过让组件的接口同时包含有一些管理Composite和Leaf的操作,客户就可以将两者一视同仁。也就是说,一个元素究竟是Composite和Leaf,对客户来说是透明的。

当然也可以采用另一种方向的设计,将接口一分为二,将责任区分开来放在不同的接口中。这样一来,设计上比较安全,但也因此失去了透明性,因为客户必须用条件语句和instanceof来判断不同类型的节点。

组合模式是一个经典的折中案例:尽管我们受到设计原则的指导,但是也需要观察某些原则对我们设计造成的影响,并故意做一些看似违反原则的设计来换取设计的平衡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值