组合模式

简介

定义: 将对象组合成树形结构以表示"部分-整体"的层次结构。

使用场景:

  • 文件系统(文件和文件夹)

  • 组织架构树

  • UI组件树

  • 菜单结构

实现要点:

  • 统一的组件接口

  • 叶子节点和容器节点

  • 递归结构

UML

代码示例

  1. 一个文件系统组件抽象接口。

public interface FileSystemComponent {
    
    /**
     * 获取名称
     */
    String getName();
    
    /**
     * 获取大小(字节)
     */
    long getSize();
    
    /**
     * 显示组件信息
     */
    void display(String prefix);
    
    /**
     * 添加子组件(仅容器节点有效)
     */
    default void add(FileSystemComponent component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }
    
    /**
     * 删除子组件(仅容器节点有效)
     */
    default void remove(FileSystemComponent component) {
        throw new UnsupportedOperationException("不支持删除操作");
    }
    
    /**
     * 获取子组件(仅容器节点有效)
     */
    default FileSystemComponent getChild(int index) {
        throw new UnsupportedOperationException("不支持获取子组件操作");
    }
    
    /**
     * 是否为容器节点
     */
    boolean isContainer();
}
  1. 组合模式的子节点

public class Folder implements FileSystemComponent {
    
    private String name;
    private List<FileSystemComponent> children;
    
    public Folder(String name) {
        this.name = name;
        this.children = new ArrayList<>();
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public long getSize() {
        long totalSize = 0;
        for (FileSystemComponent component : children) {
            totalSize += component.getSize();
        }
        return totalSize;
    }
    
    @Override
    public void display(String prefix) {
        System.out.println(prefix + "📁 " + name + " (" + formatSize(getSize()) + ")");
        for (FileSystemComponent component : children) {
            component.display(prefix + "  ");
        }
    }
    
    @Override
    public void add(FileSystemComponent component) {
        children.add(component);
    }
    
    @Override
    public void remove(FileSystemComponent component) {
        children.remove(component);
    }
    
    @Override
    public FileSystemComponent getChild(int index) {
        if (index >= 0 && index < children.size()) {
            return children.get(index);
        }
        return null;
    }
    
    @Override
    public boolean isContainer() {
        return true;
    }
    
    /**
     * 获取子组件数量
     */
    public int getChildCount() {
        return children.size();
    }
    
    /**
     * 格式化文件大小显示
     */
    private String formatSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.1f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.1f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }
}
  1. 组合模式的叶子

public class File implements FileSystemComponent {
    
    private String name;
    private long size;
    
    public File(String name, long size) {
        this.name = name;
        this.size = size;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public long getSize() {
        return size;
    }
    
    @Override
    public void display(String prefix) {
        System.out.println(prefix + "📄 " + name + " (" + formatSize(size) + ")");
    }
    
    @Override
    public boolean isContainer() {
        return false;
    }
    
    /**
     * 格式化文件大小显示
     */
    private String formatSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.1f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.1f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }
}
  1. 使用方式

public class Test {

    public static void main(String[] args) {
        System.out.println("=== 组合模式 - 文件系统示例 ===\n");

        // 创建根目录
        Folder root = new Folder("项目根目录");
        
        // 创建文档文件夹
        Folder documents = new Folder("文档");
        documents.add(new File("需求文档.docx", 2 * 1024 * 1024)); // 2MB
        documents.add(new File("设计文档.pdf", 5 * 1024 * 1024));  // 5MB
        documents.add(new File("API文档.md", 512 * 1024));         // 512KB
        
        // 创建源代码文件夹
        Folder sourceCode = new Folder("源代码");
        
        // Java源代码子文件夹
        Folder javaCode = new Folder("java");
        javaCode.add(new File("Main.java", 1024));           // 1KB
        javaCode.add(new File("Utils.java", 2048));          // 2KB
        javaCode.add(new File("Service.java", 3072));        // 3KB
        
        // 配置文件子文件夹
        Folder config = new Folder("config");
        config.add(new File("application.properties", 512)); // 512B
        config.add(new File("logback.xml", 1024));           // 1KB
        
        // 将子文件夹添加到源代码文件夹
        sourceCode.add(javaCode);
        sourceCode.add(config);
        
        // 创建资源文件夹
        Folder resources = new Folder("resources");
        resources.add(new File("logo.png", 256 * 1024));     // 256KB
        resources.add(new File("banner.jpg", 1 * 1024 * 1024)); // 1MB
        
        // 创建测试文件夹
        Folder tests = new Folder("测试");
        tests.add(new File("UnitTest.java", 1536));          // 1.5KB
        tests.add(new File("IntegrationTest.java", 2048));   // 2KB
        
        // 将主要文件夹添加到根目录
        root.add(documents);
        root.add(sourceCode);
        root.add(resources);
        root.add(tests);
        
        // 添加一些根目录下的直接文件
        root.add(new File("README.md", 1024));               // 1KB
        root.add(new File("pom.xml", 2048));                 // 2KB
        
        // 显示整个文件系统结构
        System.out.println("文件系统结构:");
        root.display("");
        
        System.out.println("\n=== 组合模式操作演示 ===");
        
        // 演示统一操作 - 计算总大小
        System.out.println("项目总大小: " + formatSize(root.getSize()));
        System.out.println("文档文件夹大小: " + formatSize(documents.getSize()));
        System.out.println("源代码文件夹大小: " + formatSize(sourceCode.getSize()));
        
        // 演示动态添加文件
        System.out.println("\n--- 动态添加文件 ---");
        FileSystemComponent newFile = new File("新增文件.txt", 1024);
        documents.add(newFile);
        System.out.println("添加新文件后,文档文件夹大小: " + formatSize(documents.getSize()));
        
        // 演示删除文件
        System.out.println("\n--- 删除文件 ---");
        documents.remove(newFile);
        System.out.println("删除文件后,文档文件夹大小: " + formatSize(documents.getSize()));
        
        // 演示遍历子组件
        System.out.println("\n--- 遍历子组件 ---");
        System.out.println("根目录下的子组件数量: " + root.getChildCount());
        for (int i = 0; i < root.getChildCount(); i++) {
            FileSystemComponent child = root.getChild(i);
            System.out.println("  " + (i + 1) + ". " + child.getName() + 
                             " (" + (child.isContainer() ? "文件夹" : "文件") + ")");
        }
        
        System.out.println("\n=== 组合模式特点总结 ===");
        System.out.println("1. 统一接口: 文件和文件夹都实现FileSystemComponent接口");
        System.out.println("2. 递归结构: 文件夹可以包含文件和子文件夹");
        System.out.println("3. 透明性: 客户端可以统一处理文件和文件夹");
        System.out.println("4. 扩展性: 易于添加新的文件系统组件类型");
    }
    
    /**
     * 格式化文件大小显示
     */
    private static String formatSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.1f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.1f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }
}

Last updated

Was this helpful?