小易说IT 小易说IT

Java 组合模式 (Composite) 示例代码

组合模式:将对象组合成树形结构以表示 “部分‑整体” 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 角色:

  1. 抽象构件 (Component):统一叶子、容器的公共接口

  2. 叶子构件 (Leaf):叶子节点,没有子节点

  3. 容器构件 (Composite):包含子节点,可以管理子元素

  4. Client:客户端,统一调用 Component 接口,不用区分叶子还是组合

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象构件 Component:文件系统抽象,文件和文件夹都实现该接口
 */
public abstract class FileSystemNode {
    protected String name;

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

    // 打印信息
    public abstract void show(int depth);

    // 容器才支持增删子节点,叶子不支持,提供默认抛出异常
    public void add(FileSystemNode node) {
        throw new UnsupportedOperationException("叶子节点不支持add操作");
    }

    public void remove(FileSystemNode node) {
        throw new UnsupportedOperationException("叶子节点不支持remove操作");
    }

    public FileSystemNode getChild(int index) {
        throw new UnsupportedOperationException("叶子节点不支持getChild操作");
    }
}

/**
 * Leaf 叶子构件:文件,没有子节点
 */
class FileLeaf extends FileSystemNode {

    public FileLeaf(String name) {
        super(name);
    }

    @Override
    public void show(int depth) {
        // 打印缩进模拟层级
        for (int i = 0; i < depth; i++) {
            System.out.print("--");
        }
        System.out.println("📄 文件:" + name);
    }
}

/**
 * Composite 容器构件:文件夹,可以包含文件/子文件夹
 */
class FolderComposite extends FileSystemNode {
    private final List<FileSystemNode> children = new ArrayList<>();

    public FolderComposite(String name) {
        super(name);
    }

    @Override
    public void add(FileSystemNode node) {
        children.add(node);
    }

    @Override
    public void remove(FileSystemNode node) {
        children.remove(node);
    }

    @Override
    public FileSystemNode getChild(int index) {
        return children.get(index);
    }

    @Override
    public void show(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("--");
        }
        System.out.println("📂 文件夹:" + name);
        // 递归打印所有子节点
        for (FileSystemNode child : children) {
            child.show(depth + 1);
        }
    }
}

/**
 * 客户端测试
 */
class Client {
    public static void main(String[] args) {
        // 构建根文件夹
        FolderComposite root = new FolderComposite("根目录");

        // 叶子文件
        FileLeaf file1 = new FileLeaf("笔记.txt");
        FileLeaf file2 = new FileLeaf("照片.jpg");

        // 子文件夹
        FolderComposite subFolder = new FolderComposite("文档");
        FileLeaf file3 = new FileLeaf("报告.docx");

        // 组装树形结构
        root.add(file1);
        root.add(file2);
        root.add(subFolder);
        subFolder.add(file3);

        // 统一调用show,不需要区分是文件还是文件夹
        root.show(0);

        System.out.println("\n--- 删除照片.jpg后 ---");
        root.remove(file2);
        root.show(0);
    }
}

输出结果

📂 文件夹:根目录
--📄 文件:笔记.txt
--📄 文件:照片.jpg
--📂 文件夹:文档
----📄 文件:报告.docx

--- 删除照片.jpg后 ---
📂 文件夹:根目录
--📄 文件:笔记.txt
--📂 文件夹:文档
----📄 文件:报告.docx

两种实现方式说明

  1. 透明模式(上面示例)

    • add/remove/getChild放在抽象父类Component中;叶子类重写抛出异常。

    • 客户端不需要区分叶子 / 组合,完全面向抽象;缺点:叶子拥有无意义的方法。

  2. 安全模式

    • add/remove/getChild只在Composite文件夹中定义;Component 只定义公共业务方法show()

    • 优点:叶子不会暴露不需要的方法;缺点客户端需要强制类型转换才能操作子节点。

安全模式简单改动示例

// Component只保留show
public abstract class FileSystemNode {
    protected String name;
    public FileSystemNode(String name){this.name=name;}
    public abstract void show(int depth);
}

// Composite容器才有add/remove
class FolderComposite extends FileSystemNode{
    public void add(FileSystemNode node){...}
}

// 客户端使用时需要强转
if(node instanceof FolderComposite folder){
    folder.add(new FileLeaf("xxx.txt"));
}

适用场景

  1. 需要表示对象整体‑部分树形结构,如文件目录、组织架构、菜单树

  2. 希望客户端忽略组合对象与单个对象差异,统一使用

优缺点

✅ 优点:

  • 高层调用简单,统一对待叶子和组合对象;

  • 易于扩展,新增叶子 / 容器,原有代码无需修改(开闭原则)。

❌ 缺点:

  • 透明模式下叶子会继承无用方法;

  • 安全模式需要类型判断,代码繁琐;

  • 层级很深时,调试复杂。


本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。

原文链接 https://yijunzhao.cc/archives/java-composite-pattern-example-code

欢迎访问 https://www.yijunzhao.cc/

https://www.yijunzhao.cc/