设计模式(10)—— 结构型 ——组合(Composite )

版权声明:如有任何疑问,欢迎@我:[email protected] https://blog.csdn.net/qq_37206105/article/details/84149833

简介

  • 定义:将对象组合成树形结构以表示“部分——整体”的层次结构。
  • 说明:组合模式使客户端对单个对象和组合对象保持一致的处理方式
  • 类型:结构型
  • 适用场景
    • 希望客户端可以忽略组合对象与单个对象的差异时
    • 处理一个树形结构时
  • 优点:
    • 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
    • 让客户端忽略了层次的差异,方便对整个层次结构进行控制
    • 简化客户端代码
    • 符合开闭原则
  • 缺点:
    • 限制类型时较为复杂
    • 使设计变得更加抽象
  • 相关设计模式
    • 组合模式和访问者模式

代码实现

业务场景:通过定义“将对象组合成树形结构”,那么我们以常见的树形结构作为简单的例子——文件(File)与目录(Folder)

我们把树形结构上的每个节点都抽象为一个组件Component。也就是说在这颗树上的节点,无论是文件(File)还是目录(Folder),都是一个组件(Component)。

于是我们可以定义组件(Component)的抽象类。让File和Folder继承自这个组件抽象类。

下面是抽象组件类Node:

/**
 * 抽象组件类:节点。
 */
public abstract class Node {

    /**
     * 获取节点的名称。
     * @return 例子中,表示Folder或者File的名称
     */
    public abstract String getName();

    /**
     * 节点的属性(简单地用String作为返回值类型)。
     * @return 例子中,表示Folder或者File的属性
     */
    public abstract String getProperty();

    /**
     * Folder 会重写这个方法
     * File 不会重写这个方法。所以默认,允许调用父类的方法
     * @param component 需要添加的子节点(子组件)
     */
    public void add(Node component){
        throw new UnsupportedOperationException("不能进行add操作");
    }

    /**
     * 跟上面的add调用方式是一样的
     * @param component 需要删除的子节点(子组件)
     */
    public void delete(Node component){
        throw new UnsupportedOperationException("不能进行delete操作");
    }

    /**
     * 打印出此节点的信息,由具体子类(File,Folder)来实现
     */
    public abstract void print();
}

文件类, 因为他也是一棵树上的节点(组建),所以继承自Node类:

/**
 * 文件类。
 */
public class File extends Node {

    // 文件名和属性
    private String name;
    private String property;

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

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

    @Override
    public String getProperty() {
        return this.property;
    }

    @Override
    public void print() {
        System.out.println( "→{File:" + this.getName() + "," + this.getProperty() + "}" );
    }

    /**
     * 我们不需要实现add,delete方法。
     * 因为文件(File)并不会存在子节点(组件)。
     * 同时File对象不能调用add,delete方法,否则会报错
     */

}

同样文件类,也继承自Node抽象组件类

/**
 * 文件组件。它同样是一个节点
 */
public class Folder extends Node {

    // 文件(Folder)的属性和名称
    private String name;
    private String property;

    // 一个目录下应该会有多个目录或者文件(一个根节点下会有多个子节点)
    private List<Node> childNode = new ArrayList<>();

    public Folder(String name, String property) {
        this.name = name;
        this.property = property;
    }

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

    @Override
    public String getProperty() {
        return this.property;
    }

    @Override
    public void print() {
        print(this, 0);
    }

    /**
     * 这里的print私有函数用来打印此目录的目录结构。
     * 如果是子节点是目录的话,递归地使用print
     * 如果子节点是文件的话,直接print就行
     * 其中打印时前缀:↓ 表示目录,→ 表示文件
     * @param node
     * @param depth
     */
    private void print(Node node, int depth){

        //为了显示目录层级关系而打印空白
        for(int i = 0 ; i < depth ; i++)
            System.out.print("  ");

        // 如果是文件,那么直接打印返回就好
        if(node instanceof File){

            node.print();
            // 或者
            // System.out.println( "→{File:" + node.getName() + "," + node.getProperty() + "}" );

            // 注意return了
            return;
        }

        // 如果是是Folder(目录)的话,↓
        System.out.println( "↓{Folder:" + node.getName() + "," + node.getProperty() + "}" );

        // 目录下如果还有Folder或者File
        for(Node child: ((Folder)node).childNode ){
            // 如果是文件,直接打印文件就好
            if(child instanceof File) {
                print(child, depth+1);
            }
            else  //如果是目录
                ((Folder)child).print(child, depth+1);
        }
    }

    @Override
    public void add(Node node) {
        childNode.add(node);
    }

    @Override
    public void delete(Node node) {
        childNode.remove(node);
    }

}

对系统进行测试,测试类Test:

/**
 * 最系统进行测试,Test类
 */
public class Test {

    public static void main(String[] args) {
        /** 测试,目录结构如下。
         *  其中 > 表示该对象为目录(Folder)
         *  反之则为文件(File)
         *  看目录应该很容易理解此树结构

        > /
           RootSummaryFile.File
           > RootEmptyFolder
           > studyFolder
               studySummary.File
               > studyEmptyFolder
               > studyJava
                   studyJavaFile.File
               > studyC
                   studyCFile.File
           > GameFolder
               gameSummary
               > gameEmptyFolder
               > game1
                   game1File.File
               > game2
                   game2File.File

        *******/

        /****************
         *下面是两个测试函数,对系统进行测试
         ***************/

        // print1,显示了文件的属性描述
        print1();

        System.out.println("==============================================");

        // print2,不显示文件的属性描述,目录结构更清晰
        print2();


    }

    public static void print1(){
        Node root = new Folder("/", "根目录");

        /******学习目录******/
        Node studyFolder = new Folder("Study", "学习目录");
        Node studySummary = new File("SummaryAboutStudy","学习目录下的总概要");

        Node studyJava = new Folder("StudyJava","学习java的学习目录");
        Node studyJavaFile = new File("StudyJavaFile.file", "学习java目录下文件");

        studyFolder.add(studySummary);

        studyJava.add(studyJavaFile);

        studyFolder.add(studyJava);

        Node studyC = new Folder("StudyC", "学习C语言的目录");
        Node studyCFile = new File("StudyCFile.file", "学习C语言目录下的文件");

        studyC.add(studyCFile);

        studyFolder.add(studyC);

        /****** 游戏目录 ******/
        Node gameFolder = new Folder("Game", "游戏目录");
        Node gameSummary = new File("SummaryAboutSummary","游戏目录下的总概要");



        Node game1 = new Folder("Game1","游戏1的目录");
        Node game1File = new File("Game1File", "游戏1目录下的文件");

        gameFolder.add(gameSummary);

        game1.add(game1File);

        gameFolder.add(game1);

        Node game2 = new Folder("Game2", "游戏2的目录");
        Node game2File = new File("Game2File", "游戏2目录下文件");

        game2.add(game2File);

        gameFolder.add(game2);

        root.add( new File("RootSummaryFile.File", "根目录下的文件") );
        root.add( new Folder("RootEmptyFolder", "根目录下的空目录") );
        root.add(studyFolder);
        root.add(gameFolder);

        root.print();
    }

    public static void print2(){
        Node root = new Folder("/", "");

        /******学习目录******/
        Node studyFolder = new Folder("Study", "");
        Node studySummary = new File("SummaryAboutStudy","");

        Node studyJava = new Folder("StudyJava","");
        Node studyJavaFile = new File("StudyJavaFile.file", "");

        studyFolder.add(studySummary);

        studyJava.add(studyJavaFile);

        studyFolder.add(studyJava);

        Node studyC = new Folder("StudyC", "");
        Node studyCFile = new File("StudyCFile.file", "");

        studyC.add(studyCFile);

        studyFolder.add(studyC);

        /****** 游戏目录 ******/
        Node gameFolder = new Folder("Game", "");
        Node gameSummary = new File("SummaryAboutSummary","");



        Node game1 = new Folder("Game1","");
        Node game1File = new File("Game1File", "");

        gameFolder.add(gameSummary);

        game1.add(game1File);

        gameFolder.add(game1);

        Node game2 = new Folder("Game2", "");
        Node game2File = new File("Game2File", "");

        game2.add(game2File);

        gameFolder.add(game2);

        root.add( new File("RootSummaryFile.File", "") );
        root.add( new Folder("RootEmptyFolder", "") );
        root.add(studyFolder);
        root.add(gameFolder);

        root.print();
    }

}

输出测试用例:

{Folder:/,根目录}{File:RootSummaryFile.File,根目录下的文件}{Folder:RootEmptyFolder,根目录下的空目录}{Folder:Study,学习目录}{File:SummaryAboutStudy,学习目录下的总概要}{Folder:StudyJava,学习java的学习目录}{File:StudyJavaFile.file,学习java目录下文件}{Folder:StudyC,学习C语言的目录}{File:StudyCFile.file,学习C语言目录下的文件}{Folder:Game,游戏目录}{File:SummaryAboutSummary,游戏目录下的总概要}{Folder:Game1,游戏1的目录}{File:Game1File,游戏1目录下的文件}{Folder:Game2,游戏2的目录}{File:Game2File,游戏2目录下文件}
======================={Folder:/,}{File:RootSummaryFile.File,}{Folder:RootEmptyFolder,}{Folder:Study,}{File:SummaryAboutStudy,}{Folder:StudyJava,}{File:StudyJavaFile.file,}{Folder:StudyC,}{File:StudyCFile.file,}{Folder:Game,}{File:SummaryAboutSummary,}{Folder:Game1,}{File:Game1File,}{Folder:Game2,}{File:Game2File,}

猜你喜欢

转载自blog.csdn.net/qq_37206105/article/details/84149833
今日推荐