Skip to content

组合模式

ZHI-XINHUA edited this page Jan 11, 2019 · 1 revision

组合模式

组合模式将对象组织到树结构中,可以用来描述整体与部分的关系。

忽略各个角色的细节,下图的角色结构如下:

  • 抽象构件(Component)角色:给参加组合的对象规定一个接口,规范共有的接口及默认行为。这个接口可以用来管理所以的子对象,要提供一个接口 以规范取得和管理下层组件的接口,包括add()、remove()已经getChild()之类的方法

  • 树叶构件(Leaf)角色:代办参加组合的树叶对象。一个树叶没有下级的子对象。定义出参加组合的原始对象行为。

  • 树枝构件(Composite)角色:代表参加组合的有子对象的对象,并给出树枝构件对象的行为

组合模式分为安全式和透明式

1、透明式

​ 在Component里面声明所有用来管理子类对象的方法,包括add()、remove()和getChild()方法。这样做的好处是所有的构建类都有相同的接口。在客户端看来,树叶类对象与组合类对象的区别在接口层次上消失了,客户端可以同等对待所有对象,这就是透明形式的组合模式。

​ 缺点是不够安全,树叶类对象与组合类对象本质上是有区别的,因此add()、remove()和getChild()方法对于树叶字节是没有意义的。虽然编译时期不会出错,但是在运行时期会出错。

uml 结构图如下

例子:简单实现文件夹和文件操作

**(1)抽象文件 IFile **

/**
 * 透明模式
 *  文件接口
 *  抽象构件角色:给参加组合的对象规定一个接口,规范共有的接口及默认行为。这个接口可以用来管理所以的子对象,要提供一个接口
 *  以规范取得和管理下层组件的接口,包括add()、remove()已经getChild()之类的方法
 */
public interface IFile {
    /********1、有的接口及默认行为:公共行为,只是简单列出几个方法**********/
    //获取文件名称
    String getFileName();
    //删除文件
    void delete();

    /********2、管理下层组件:文件夹才有的行为,只是简单列出几个方法****************/
    //创建文件
    void createFile(IFile iFile);
    //创建文件夹
    void createFolder(IFile iFile);
    //删除文件
    void deleteFile(IFile iFile);
    //获取文件夹下所有文件
    List<IFile> getFiles();

}

(2)文件类 File

/**
 *  透明模式
 *  文件
 *  树叶构件(Leaf)角色:代办参加组合的树叶对象。一个树叶没有下级的子对象。定义出参加组合的原始对象行为。
 */
public class File implements IFile{
    private String fileName;

    public File(String fileName){
        this.fileName = fileName;
    }
    @Override
    public String getFileName() {
        return fileName;
    }

    @Override
    public void delete() {
        //假装删除
        fileName ="";
    }

    /**
     *  文件夹特有方法,File不支持的方法
     */
    @Override
    public void createFile(IFile iFile) {
        throw new UnsupportedOperationException();
    }

    /**
     *  文件夹特有方法,File不支持的方法
     */
    @Override
    public void createFolder(IFile iFile) {
        throw new UnsupportedOperationException();
    }

    /**
     *  文件夹特有方法,File不支持的方法
     */
    @Override
    public void deleteFile(IFile iFile) {
        throw new UnsupportedOperationException();
    }

    /**
     *  文件夹特有方法,File不支持的方法
     */
    @Override
    public List<IFile> getFiles() {
        throw new UnsupportedOperationException();
    }
}

(3)文件夹类 Folder

/**
 * 文件夹
 * 树枝构件(Composite)角色:代表参加组合的有子对象的对象,并给出树枝构件对象的行为
 */
public class Folder implements IFile{
    private String folderName;

    private List<IFile> files;

    public Folder(String folderName,List<IFile> files){
        this.folderName = folderName;
        this.files = files;
    }

    @Override
    public String getFileName() {
        return folderName;
    }

    @Override
    public void delete() {
        files = null;
    }

    @Override
    public void createFile(IFile iFile) {
        files.add(iFile);
    }

    @Override
    public void createFolder(IFile iFile) {
        files.add(iFile);
    }

    @Override
    public void deleteFile(IFile iFile) {
        files.remove(iFile);
    }

    @Override
    public List<IFile> getFiles() {
        return files;
    }
}

代码可看出,文件类File 存在 文件夹的特有方法,这是没必要而且调用时候会抛出异常。所以,以下安全式解决了这问题。

2、安全式

    Component里面声明公共方法,这样的做法安全。编译时期不会出错,运行时期也不会出错。但是不够透明度。

uml结构图如下

例子改造

(1)抽象文件 IFile

去掉文件夹特有的方法,保留公共方法

/**
 * Created by xh.zhi on 2018-9-13.
 * 安全模式
 *  文件接口
 *  抽象构件角色:给参加组合的对象规定一个接口,规范共有的接口及默认行为。这个接口可以用来管理所以的子对象,要提供一个接口
 *  以规范取得和管理下层组件的接口,包括add()、remove()已经getChild()之类的方法
 */
public interface IFile {
    /********有的接口及默认行为:公共行为,只是简单列出几个方法**********/
    //获取文件名称
    String getFileName();
    //删除文件
    void delete();
    
}

(2)文件 File

/**
 *  安全模式
 *  文件
 *  树叶构件(Leaf)角色:代办参加组合的树叶对象。一个树叶没有下级的子对象。定义出参加组合的原始对象行为。
 */
public class File implements IFile {
    private String fileName;

    public File(String fileName){
        this.fileName = fileName;
    }
    @Override
    public String getFileName() {
        return fileName;
    }

    @Override
    public void delete() {
        //假装删除
        fileName ="";
    }
    
}

测试类

public class MainTest {
    public static void main(String[] args) {
        List<IFile> files1 = new ArrayList<IFile>();
        List<IFile> files2 = new ArrayList<IFile>();
        //模拟文件夹下面的文件
        for(int i=1;i<=5;i++){
            files1.add(new File("test"+i+".java"));
            files2.add(new File("速度与激情"+i+".avi"));
        }
        Folder movie = new Folder("我的电影",files2);
        //创建root
        Folder c = new Folder("C盘",files1);
        //创建文件
        c.createFile(new File("需求文档.doc"));
        //创建子文件夹
        c.createFolder(movie);

        List<IFile> list = c.getFiles();
        for (IFile node:list) {
            //子文件夹
            if(node instanceof Folder){
                System.out.println("======"+node.getFileName()+"========");
                Folder folder = (Folder) node;
                List<IFile> cnodes = folder.getFiles();
                for (IFile cnode:cnodes) {
                    System.out.println(cnode.getFileName());
                }
                System.out.println("======"+node.getFileName()+"========");
            }else{
                System.out.println(node.getFileName());
            }

        }
    }
}

运行结果:

test1.java
test2.java
test3.java
test4.java
test5.java
需求文档.doc
======我的电影========
速度与激情1.avi
速度与激情2.avi
速度与激情3.avi
速度与激情4.avi
速度与激情5.avi
======我的电影========