设计模式之复合模式

来源:互联网 发布:胡一帆写字软件 编辑:程序博客网 时间:2024/05/16 01:40

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来判断不同类型的节点。

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

0 0
原创粉丝点击