组合模式——初学JAVA设计模式

一、基本概念

树形结构在软件中随处可见,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结构等等。

组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。

(1)组合关系:佳丽三千只属于一个皇帝。
(2)聚合关系:一个老师有很多学生,一个学生有多个老师。

二、角色组成

  • Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
  • Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
  • Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

三、实例代码

  • 抽象构件角色
package cpmposite03;

/**
 * @author:xiaofa
 * @date2020/4/2811:12
 * @Description:抽象构件
 *
 */
abstract class AbstractFile {
    public abstract void add(AbstractFile file);
    public abstract void remove(AbstractFile file);
    public abstract AbstractFile getChild(int i);
    public abstract void killVirus();
    }
  • 叶子构件:ImageFIle.java
package cpmposite03;

/**
 * @author:xiaofa
 * @date2020/4/2811:14
 * @Description:叶子构件
 *
 */
public class ImageFile extends AbstractFile{
    //名字属性
    private String name;

    //一个参数的构造方法
    public ImageFile(String name) {
        this.name = name;
    }

    @Override
    public void add(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    @Override
    public void remove(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    @Override
    public AbstractFile getChild(int i) {
        System.out.println("对不起,不支持该方法!");
        return null;
    }

    @Override
    public void killVirus() {
        System.out.println("----对图像文件----"+name+"进行杀毒");

    }
}
  • 叶子构件:TextFile.java
package cpmposite03;

/**
 * @author:xiaofa
 * @date2020/4/2811:14
 * @Description:叶子构件
 *
 */
public class TextFile extends AbstractFile{
    //名字属性
    private String name;

    //一个参数的构造方法
    public TextFile(String name) {
        this.name = name;
    }

    @Override
    public void add(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    @Override
    public void remove(AbstractFile file) {
        System.out.println("对不起,不支持该方法!");
    }

    @Override
    public AbstractFile getChild(int i) {
        System.out.println("对不起,不支持该方法!");
        return null;
    }

    @Override
    public void killVirus() {
        System.out.println("----对文本文件----"+name+"进行杀毒");

    }
}
  • 叶子构件:Video.java(同上此处省略)
...
@Override
    public void killVirus() {
        System.out.println("----对文本文件----"+name+"进行杀毒");
...
  • 容器构件:Folder.java
package cpmposite03;

import composite02.Composite;

import java.util.ArrayList;

/**
 * @author:xiaofa
 * @date2020/4/2811:18
 * @Description:容器构件
 */
 class Folder extends AbstractFile {

    //名字属性
    private String name;

    //一个参数的构造方法
    public Folder(String name) {
        this.name = name;
    }

    //构建容器,用于存储AbstactFile类型的成员
    ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>();


    @Override
    public void add(AbstractFile file) {
        fileList.add(file);
    }

    @Override
    public void remove(AbstractFile file) {
        fileList.remove(file);
    }

    @Override
    public AbstractFile getChild(int i) {
        return (AbstractFile)fileList.get(i);
    }

    @Override
    public void killVirus() {
        System.out.println("----对文件夹----"+name+"进行杀毒");

        for (Object object:fileList){
            ((AbstractFile)object).killVirus();
        }

    }
}
  • 客户端:Client.java
package cpmposite03;

import composite01.Folder;
import composite01.ImageFile;
import composite01.TextFile;

/**
 * @author:xiaofa
 * @date2020/4/2715:15
 * @Description:
 */
public class Client {

    public static void main(String[] args) {
        Folder folder1 , folder2 , folder3 ;

        folder1 = new Folder("XiaoFa的资料");
        folder2 = new Folder("图像文件");
        folder3 = new Folder("文本文件");

        ImageFile image1 , image2 , image3;
        image1 = new ImageFile("小女.jpg");
        image2 = new ImageFile("杨过河.jpg");

        TextFile text1 ,text2;
        text1 = new TextFile("菊花宝典.txt");
        text2 = new TextFile("九阴真茎.dox");

        folder2.addImageFile(image1);
        folder2.addImageFile(image2);
        folder3.addTextFile(text1);
        folder3.addTextFile(text2);
        folder1.addFolder(folder2);
        folder1.addFolder(folder3);

        folder3.killVirus();
        image1.killVirus();
    }
}

运行结果

  • 对folder1进行消毒
    在这里插入图片描述

  • 对folder3和image1进行消毒
    在这里插入图片描述

四、透明组合和安全组合模式

由于AbstractFile中声明了大量用于管理和访问成员构件的方法,如add(),remove等,因此要新增文件类实现这些方法,以提供错误提示和异常处理。这里给出两种实现方式。

  • 方式一:将叶子构件的add()、remove()的实现代码一直抽象类中,然后将成员构件的相关的代码删除。
package composite04_1;
abstract class AbstractFile {
    public void add(AbstractFile file){
        System.out.println("对不起,不支持该方法!");
    };
    public void remove(AbstractFile file){
        System.out.println("对不起,不支持该方法!");
    };
    public AbstractFile getChild(int i){
        System.out.println("对不起,不支持该方法!");
        return null;
    };
    public abstract void killVirus();
    }
  • 方式二:在抽象构件AbstractFile中不声明访问和管理成员的方法。
package composite04_1;
abstract class AbstractFile_2 {
    public abstract void killVirus();
    }

小结

  • 透明组合模型:
    方式一是透明组合模型,抽象构件中声明所有用于管理成员对象的方法,保证了所有构建类具有相同的接口,是组合模式的标准形式。
    但缺点不够安全,因为叶子对象不可能有下一层次的对象,因此add()、remove()等方法没有意义,编译不出错,运行和调用可能出错。
  • 安全组合模型:
    方式二是安全组合模型,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法。做法安全,因为根本不像叶子对象提供这些管理方法,叶子对象客户端都不会调用这些方法。
    但缺点不够透明,因为叶子构件和容器构件具有不同的方法,容器中方法没有在抽象构建中定义,客户端不能完全针对抽象编程。

适用场景

  • 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
  • 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
  • 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。

参考书籍《Java设计模式-刘伟第二版》

猜你喜欢

转载自blog.csdn.net/fazijiaidama/article/details/105815370