Skip to content

组合模式

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构以表示“部分-整体”的层次结构。通过让客户端以统一的方式处理单个对象和组合对象,实现了对树形结构中所有节点的透明化操作。

特点 (Characteristics)

  1. 统一接口:客户端可以一致地使用简单元素(叶子)和复杂元素(容器),无需关心处理的是单个对象还是对象的组合。
  2. 形成树形结构:能够清晰地表示出对象之间的层级关系,如文件系统、组织架构、菜单系统等。
  3. 递归组合:容器对象可以包含其他容器对象或叶子对象,形成递归的树状结构。
  4. 易于扩展:可以方便地在树中增加新的组件(叶子或容器),而不需要修改现有客户端代码。
  5. 符合开闭原则:对扩展开放(增加新类型的组件),对修改关闭(客户端代码通常无需修改)。

核心组成 (Core Components)

  1. 组件 (Component):
    • 为组合中的对象声明统一的接口
    • 可以定义一些默认行为(可选)。
    • 声明访问及管理子组件的方法(如 add(), remove(), getChild()),通常在抽象类或接口中声明,但在叶子节点中可能为空实现或抛出异常。
  2. 叶子 (Leaf):
    • 表示组合中的叶节点对象,即没有子节点的对象。
    • 实现 Component 接口,但不提供添加、删除子组件等管理子节点的方法的具体实现(因为叶子不能有子节点)。
    • 定义了叶子对象的行为。
  3. 容器 (Composite):
    • 表示组合中的分支节点对象,即可以包含子节点的对象。
    • 实现 Component 接口,并在内部维护一个子组件(Component)的集合。
    • 实现管理子组件的方法(add, remove, getChild等),并通常通过递归调用其子组件的相应方法来实现自己的功能。

核心思想 (Core Idea)

“让部分与整体具有相同的接口”。组合模式的核心在于,无论是单个对象(叶子)还是由多个对象组成的复合对象(容器),它们都实现了同一个接口。客户端在使用这些对象时,不需要区分它们是“原子”还是“容器”,可以统一地调用接口方法。对于容器,该方法的实现通常是递归地调用其所有子组件的同一方法。

使用场景 (Use Cases)

场景描述说明
表示部分-整体的树形结构当需要表示对象的层次结构,且该结构呈现出“整体由部分组成,部分又可能由更小的部分组成”的特点时。
希望客户端统一处理单个对象和组合对象客户端代码希望以相同的方式处理树中的任何节点,无论它是叶子还是分支。
文件系统目录(容器)包含文件(叶子)和子目录(容器),文件和目录都可以有名称、大小、路径等属性,也可以进行删除、复制等操作。
图形用户界面 (GUI)窗口(容器)包含按钮、文本框(叶子)和面板(容器),它们都可以被绘制、移动、处理事件。
组织架构公司(容器)包含部门(容器)和员工(叶子),它们都可以有名称、负责人、计算成本等。
菜单系统菜单(容器)包含菜单项(叶子)和子菜单(容器),它们都可以被点击、显示、启用/禁用。

实现示例

文件组件-抽象组件

java
public interface FileComponent {

    void addFile(FileComponent fileComponent);

    void removeFile(FileComponent fileComponent);

    String getName();

    void reName(String newName);

}

文件-叶子节点

java
@Slf4j
public class File implements FileComponent {

    private String fileName;

    public File(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void addFile(FileComponent fileComponent) {
        log.info("文件不支持添加子文件");
    }

    @Override
    public void removeFile(FileComponent fileComponent) {
        log.info("文件不支持删除子文件");
    }

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

    @Override
    public void reName(String newName) {
        this.fileName = newName;
    }
}

文件夹-复合节点

java
@Slf4j
public class FileFolder implements FileComponent {
    private String folderName;

    private List<FileComponent> fileComponentList = new ArrayList<>();

    public FileFolder(String folderName) {
        this.folderName = folderName;
    }


    @Override
    public void addFile(FileComponent fileComponent) {
        fileComponentList.add(fileComponent);
    }


    @Override
    public void removeFile(FileComponent fileComponent) {
        fileComponentList.remove(fileComponent);
    }

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

    @Override
    public void reName(String newName) {
        this.folderName = newName;
    }

    public void ls() {
        log.info("-{}", this.folderName);
        for (FileComponent fileComponent : fileComponentList) {
           if(fileComponent instanceof FileFolder) {
               ((FileFolder) fileComponent).ls();
           }else {
                log.info("- {}", fileComponent.getName());
           }
        }
    }
}

调用-客户端

java
public class Main {
    public static void main(String[] args) {
        FileFolder fileFolder = new FileFolder("/");

        FileFolder images = new FileFolder("images");
        FileFolder docs = new FileFolder("docs");

        File img1 = new File("img1.png");
        File img2 = new File("img2.jpg");
        File doc1 = new File("doc1.txt");
        File doc2 = new File("doc2.pdf");

        fileFolder.addFile(images);
        fileFolder.addFile(docs);

        images.addFile(img1);
        images.addFile(img2);
        docs.addFile(doc1);
        docs.addFile(doc2);

        fileFolder.ls();
    }
}

打印

bash
18:10:24.176 [main] INFO com.lj.composite.FileFolder -- -/
18:10:24.179 [main] INFO com.lj.composite.FileFolder -- -images
18:10:24.179 [main] INFO com.lj.composite.FileFolder -- - img1.png
18:10:24.179 [main] INFO com.lj.composite.FileFolder -- - img2.jpg
18:10:24.179 [main] INFO com.lj.composite.FileFolder -- -docs
18:10:24.179 [main] INFO com.lj.composite.FileFolder -- - doc1.txt
18:10:24.179 [main] INFO com.lj.composite.FileFolder -- - doc2.pdf
最近更新