【设计模式】通过简单案例学习组合模式

311 阅读5分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

组合模式

  组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。   可以通过一个或者多个简单的对象,经过组合之后,生成一个新的对象,原来的对象是这个对象的元素。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

  组合模式也可以看成是对某些对象的迭代调用的方式。

  组合模式优点:

  • 1、高层模块调用简单。
  • 2、节点自由增加。

案例分析

  下面通过一个大家都熟悉的示例,进行组合模式的初步理解。这个示例来源于往年的软考下午试题。

  如下图预构造一个文件目录树,就是我们常用的文件夹和文件的关系类似的。文件夹里面可以包含文件和文件夹,二层文件夹下又可以包含文件和文件夹。其中一层文件夹是二层文件夹和文件的父类;而三层文件夹或者文件又是二层文件夹的子类。类似这样的树状结构, 采用组合设计模式来设计。 图片.png

分析

  性和动作,不管文件和文件夹,都有名称、大小、格式,都可以对文件和文件夹进行操作,包含修改、删除、移动等操作方式。那么可以将这些公共的操作和属性值提取出来一个抽象类。然后用一个子属性来代替下层数据信息。

开发

  可以从图中看到,针对公共属性和方法进行提取之后,可以创建一个AbstractFile的抽象类。其中包含:文件夹、文件的名称;添加文件或者文件夹的方法;删除文件或者文件夹的方法;移动文件或者文件夹的方法;获取文件子文件列表的方法。 AbstractFile的抽象类如下:

/**
 * @ClassName AbstractFile
 * @Description:
 * @Author 公众号:Java全栈架构师
 * @Version V1.0
 **/
public abstract class AbstractFile {
    protected String fileName;

    /**
     * 输出文件、文件夹名称
     * */
    public void printFileName(){
        System.out.println(fileName);
    }

    /**
     * 添加文件或者文件夹
     * */
    public abstract boolean addChild(AbstractFile abstractFile);

    /**
     * 删除文件或者文件夹
     * */
    public abstract boolean delChild(AbstractFile abstractFile);

    /**
     * 移动文件或者文件夹
     * */
    public abstract boolean removeChild(AbstractFile abstractFile);

    /**
     * 获取文件子文件列表
     * */
    public abstract List<AbstractFile> getChildAbstractFileList();
}

  现在编写文件的具体实现类,因为文件没有子文件或文件夹,所以我们子啊获取文件子文件列表的时候,默认就返回null即可。这一点需要注意,和我们业务中的点一样,在编写代码实现的时候,对业务的基本流程和属性是需要一定了解的。其中在方法返回值之前,可以加入实际的业务逻辑,如果成功就返回true;如果处理失败,就返回false。系统会默认的返回false;

  文件File的类如下:

/**
 * @ClassName File
 * @Description:
 * @Author 公众号:Java全栈架构师
 * @Version V1.0
 **/
public class File extends AbstractFile {

    public File(String name){
        this.fileName = name;
    }
    @Override
    public boolean addChild(AbstractFile abstractFile) {
        return false;
    }

    @Override
    public boolean delChild(AbstractFile abstractFile) {
        return false;
    }

    @Override
    public boolean removeChild(AbstractFile abstractFile) {
        return false;
    }

    @Override
    public List<AbstractFile> getChildAbstractFileList() {
        return null;
    }
}

  在创建文件夹的下的子文件或文件的集合的时候,需要注意去获取文件夹下的子文件和子文件夹下的文件和文件夹,需要层层迭代下去,本文作为示例,直接使用new 去创建了。这点需要注意。

  Folder文件夹的类如下:

/**
 * @ClassName Folder
 * @Description:
 * @Author 公众号:Java全栈架构师
 * @Version V1.0
 **/
public class Folder extends AbstractFile {
    private List<AbstractFile> list;

    public Folder(String name) {
        this.fileName = name;
        this.list = new ArrayList<AbstractFile>();
    }

    @Override
    public boolean addChild(AbstractFile abstractFile) {
        this.list.add(abstractFile);
        return false;
    }

    @Override
    public boolean delChild(AbstractFile abstractFile) {
        return false;
    }

    @Override
    public boolean removeChild(AbstractFile abstractFile) {
        return false;
    }

    @Override
    public List<AbstractFile> getChildAbstractFileList() {
        return list;
    }
}

运行

  以上基础模块已经搭建完成了,包含文件夹Folder和文件File两个。他们都有共同的方法。那么现在开始变写一下 遍历目录下的文件和文件夹名称的方法,输出文件名和文件夹名称。

  首先初始化模拟一个文件夹及文件的目录格式:其中根目录是:D:\,下面有几个文件夹分别为:“Linux”和“Win”两个,这两个文件夹下还有下层子集文件和文件夹。编写一个输出文件和文件夹名称的方法printFileTree,由于整个目录树都是基于文件夹Folder和文件File两个构建的,因此在输出过程中需要迭代使用printFileTree,具体方法如下:

/**
 * @ClassName Folder
 * @Description:
 * @Author 公众号:Java全栈架构师
 * @Version V1.0
 **/
public class MainApp {
    public static void main(String[] args) {
        AbstractFile rootFolder =new Folder("D:\\");
        AbstractFile linuxFolder =new Folder("Linux");
        AbstractFile winFolder =new Folder("Win");
        AbstractFile jdkFolder =new Folder("jdk");
        AbstractFile testFile =new File("test.java");
        AbstractFile linuxTestFile =new File("Linux_test.java");
        rootFolder.addChild(linuxFolder);
        rootFolder.addChild(winFolder);
        winFolder.addChild(jdkFolder);
        jdkFolder.addChild(testFile);
        linuxFolder.addChild(linuxTestFile);
        printFileTree(rootFolder);
    }

    public static void printFileTree(AbstractFile abstractFile) {
        abstractFile.printFileName();
        List<AbstractFile> abstractFileList = abstractFile.getChildAbstractFileList();
        if (abstractFileList == null) {
            return;
        } else {
            for (AbstractFile abstractFiles : abstractFileList) {
                printFileTree(abstractFiles);
            }
        }
    }
}

  运行之后输出结果如下,可以看到文件目录的层级信息。

D:\
Linux
Linux_掘金.java
Win
jdk
test_掘金.java

图片.png

结语

  好了,一个简单的组合模式就介绍完成了,学习设计模式的时候,尽量结合熟悉的案例进行分析,这样的话就会有事半功倍的奇效,也更容易理解。大家可以尝试着在项目中使用设计模式的方式进行开发。

  感谢您的阅读,希望您喜欢,如对您有帮助,欢迎点赞收藏。如有不足之处,欢迎评论指正。下次见。

  作者介绍:【小阿杰】一个爱鼓捣的程序猿,JAVA开发者和爱好者。公众号【Java全栈架构师】维护者,欢迎关注阅读交流。