【设计模式-3.7】结构型——组合模式

说明:本文介绍结构型设计模式之一的组合模式

定义

组合模式(Composite Pattern)又叫作整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型设计模式。(引自《设计模式就该这样学》P263)

文件系统

以文件系统为例,如下,是服务器上某个文件夹的文件结构,该文件夹下既有文件夹,也有文件

如果我要构建这样一个文件夹-文件系统,代码应该是这样写的,如下:

(图片文件,ImageFile)

java 复制代码
/**
 * 图片文件
 */
public class ImageFile {

    /**
     * 图片名称
     */
    private String name;

    public ImageFile(String name) {
        this.name = name;
    }

    /**
     * 展示
     */
    public void show(int space) {
        for (int i = 0; i < space; i++) {
            System.out.print(" ");
        }
        System.out.println(name);
    }
}

(电影文件,MovieFile)

java 复制代码
/**
 * 电影文件
 */
public class MovieFile {

    /**
     * 电影名称
     */
    private String name;

    public MovieFile(String name) {
        this.name = name;
    }

    /**
     * 展示
     */
    public void show(int space) {
        for (int i = 0; i < space; i++) {
            System.out.print(" ");
        }
        System.out.println(name);
    }
}

(文件夹,Folder,定义多个文件集合,并开放对应的增加方法)

java 复制代码
import java.util.ArrayList;

/**
 * 文件夹
 */
public class Folder {
    /**
     * 文件夹名称
     */
    private String name;

    /**
     * 文件夹下的文件夹
     */
    private ArrayList<Folder> folders = new ArrayList<>();

    /**
     * 文件夹下的图片文件
     */
    private ArrayList<ImageFile> imageFiles = new ArrayList<>();

    /**
     * 文件夹下的视频文件
     */
    private ArrayList<MovieFile> movieFiles = new ArrayList<>();

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

    /**
     * 添加文件夹
     */
    public void addFolder(Folder folder) {
        folders.add(folder);
    }

    /**
     * 添加图片文件
     */
    public void addImageFile(ImageFile imageFile) {
        imageFiles.add(imageFile);
    }

    /**
     * 添加电影文件
     */
    public void addMoveFile(MovieFile movieFile) {
        movieFiles.add(movieFile);
    }

    /**
     * 展示
     */
    public void show(int space) {
        // 打印文件夹
        space++;
        for (int i = 0; i < space; i++) {
            System.out.print(" ");
        }
        System.out.println(name);
        for (Folder folder : folders) {
            folder.show(space);
        }

        // 打印图片文件
        space++;
        for (ImageFile imageFile : imageFiles) {
            imageFile.show(space);
        }

        // 打印电影文件
        space++;
        for (MovieFile movieFile : movieFiles) {
            movieFile.show(space);
        }
    }
}

(客户端使用,Client)

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 顶级文件夹
        Folder folder = new Folder("folder");

        // 二级文件夹
        Folder images = new Folder("images");
        Folder movies = new Folder("movies");

        // 三级目录下的文件
        ImageFile boy = new ImageFile("boy.png");
        ImageFile girl = new ImageFile("girl.png");
        images.addImageFile(boy);
        images.addImageFile(girl);

        // 三级文件夹
        Folder director1 = new Folder("heizeming");
        Folder director2 = new Folder("xiaolinzhengshu");

        // 四级目录
        MovieFile movieFile1 = new MovieFile("luoshengmen.mp4");
        MovieFile movieFile2 = new MovieFile("qiwushi.mp4");
        director1.addMoveFile(movieFile1);
        director1.addMoveFile(movieFile2);

        MovieFile movieFile3 = new MovieFile("duomingjian.mp4");
        MovieFile movieFile4 = new MovieFile("qiefu.mp4");
        director2.addMoveFile(movieFile3);
        director2.addMoveFile(movieFile4);
        movies.addFolder(director1);
        movies.addFolder(director2);

        // 文件夹添加到顶级文件夹
        folder.addFolder(images);
        folder.addFolder(movies);
        folder.show(0);
    }
}

运行,hora!(看!),能实现目的

但这里存在问题,文件系统,简单来说只有文件夹和文件两个实体对象,代码中的图片文件、电影文件可以抽象为文件(File),这是整体与个体的场景。

组合模式

使用组合模式改进上述代码,如下:

(抽象节点类,Node)

java 复制代码
/**
 * 抽象节点
 */
public abstract class Node {

    /**
     * 节点名称
     */
    protected String name;

    public Node(String name) {
        this.name = name;
    }

    /**
     * 添加节点
     */
    protected abstract void add(Node node);

    /**
     * 展示
     */
    protected void show(int space) {
        for (int i = 0; i < space; i++) {
            System.out.print(" ");
        }
        System.out.println(name);
    }

    /**
     * 重载方法,使用的时候就不用给参数了
     */
    protected void show() {
        show(0);
    }
}

(文件夹,Folder)

java 复制代码
import java.util.ArrayList;

/**
 * 文件夹
 */
public class Folder extends Node {

    /**
     * 文件夹下的子节点
     */
    private ArrayList<Node> childrenNodes = new ArrayList<>();

    /**
     * 调用父类的构造方法
     */
    public Folder(String name) {
        super(name);
    }

    @Override
    protected void add(Node node) {
        childrenNodes.add(node);
    }

    @Override
    protected void show(int space) {
        super.show(space);
        space++;
        for (Node node : childrenNodes) {
            node.show(space);
        }
    }
}

(文件,File)

java 复制代码
/**
 * 文件
 */
public class File extends Node {

    /**
     * 调用父类的构造方法
     */
    public File(String name) {
        super(name);
    }

    @Override
    protected void add(Node node) {
        System.out.println("不能添加子节点");
    }

    @Override
    protected void show(int space) {
        super.show(space);
    }
}

(客户端使用,Client)

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 顶级文件夹
        Folder folder = new Folder("folder");

        // 二级文件夹
        Folder images = new Folder("images");
        Folder movies = new Folder("movies");

        // 三级目录下的文件
        File boy = new File("boy.png");
        File girl = new File("girl.png");
        images.add(boy);
        images.add(girl);

        // 三级文件夹
        Folder director1 = new Folder("heizeming");
        Folder director2 = new Folder("xiaolinzhengshu");

        // 四级目录
        File movieFile1 = new File("luoshengmen.mp4");
        File movieFile2 = new File("qiwushi.mp4");
        director1.add(movieFile1);
        director1.add(movieFile2);

        File movieFile3 = new File("duomingjian.mp4");
        File movieFile4 = new File("qiefu.mp4");
        director2.add(movieFile3);
        director2.add(movieFile4);
        movies.add(director1);
        movies.add(director2);

        // 文件夹添加到顶级文件夹
        folder.add(images);
        folder.add(movies);
        folder.show();
    }
}

执行如下,也实现了目的

这么看下来,文件系统场景使用组合模式实现是很不错的,代码少了很多,也削减了文件夹类中的职责(可以对比下Folder类前后的代码)

使用场景

在《设计模式就该这样学》(P229)这本书中,提到状态模式适用于以下场景:

(1)希望客户端可以忽略组合对象与单个对象的差异;

(2)对象层次具备整体和部分,呈树形结构;

除了文件系统、企业组织架构场景,我还没想到其他使用场景;

总结

本文介绍了结构型设计模式中的组合模式,参考《设计模式就该这样学》、《秒懂设计模式》、《设计模式的艺术》(第一版)这三本书,其中的例子来自《秒懂设计模式》。

相关推荐
BillKu26 分钟前
Java + Spring Boot + Mybatis 实现批量插入
java·spring boot·mybatis
YuTaoShao28 分钟前
Java八股文——集合「Map篇」
java
有梦想的攻城狮2 小时前
maven中的maven-antrun-plugin插件详解
java·maven·插件·antrun
硅的褶皱6 小时前
对比分析LinkedBlockingQueue和SynchronousQueue
java·并发编程
MoFe16 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore
季鸢7 小时前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
Fanxt_Ja7 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
蔡蓝7 小时前
设计模式-迪米特法则
设计模式·log4j·迪米特法则
Mr Aokey7 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring
小马爱记录8 小时前
sentinel规则持久化
java·spring cloud·sentinel