Skip to content

组合模式 (Composite Pattern) ​

模式定义 ​

组合模式是一种结构型设计模式,它将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

核心概念 ​

  • Component(组件): 为叶子和容器声明统一接口
  • Leaf(叶子): 没有子节点的节点
  • Composite(容器): 包含子节点的节点

实现示例:文件系统 ​

组件接口 ​

java
// 文件系统组件接口
public abstract class FileSystemComponent {
    protected String name;

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

    // 获取大小
    public abstract int getSize();

    // 显示结构
    public abstract void display(String prefix);

    // 以下操作仅对容器有意义,叶子实现抛出异常
    public void add(FileSystemComponent component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }

    public void remove(FileSystemComponent component) {
        throw new UnsupportedOperationException("不支持移除操作");
    }

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

    public String getName() {
        return name;
    }
}

叶子节点(文件) ​

java
// 文件
public class File extends FileSystemComponent {
    private final int size; // 文件大小(KB)

    public File(String name, int size) {
        super(name);
        this.size = size;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public void display(String prefix) {
        System.out.println(prefix + "📄 " + name + " (" + size + "KB)");
    }
}

容器节点(文件夹) ​

java
// 文件夹
public class Folder extends FileSystemComponent {
    private final List<FileSystemComponent> children = new ArrayList<>();

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

    @Override
    public void add(FileSystemComponent component) {
        children.add(component);
    }

    @Override
    public void remove(FileSystemComponent component) {
        children.remove(component);
    }

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

    @Override
    public int getSize() {
        int totalSize = 0;
        for (FileSystemComponent child : children) {
            totalSize += child.getSize();
        }
        return totalSize;
    }

    @Override
    public void display(String prefix) {
        System.out.println(prefix + "📁 " + name + " (" + getSize() + "KB)");
        for (FileSystemComponent child : children) {
            child.display(prefix + "  ");
        }
    }
}

使用示例 ​

java
public class CompositeDemo {
    public static void main(String[] args) {
        // 创建文件
        FileSystemComponent file1 = new File("README.md", 5);
        FileSystemComponent file2 = new File("Main.java", 15);
        FileSystemComponent file3 = new File("Utils.java", 8);
        FileSystemComponent file4 = new File("config.yml", 2);
        FileSystemComponent file5 = new File("style.css", 12);
        FileSystemComponent file6 = new File("index.html", 6);

        // 创建文件夹
        Folder srcFolder = new Folder("src");
        srcFolder.add(file2);
        srcFolder.add(file3);

        Folder resourcesFolder = new Folder("resources");
        resourcesFolder.add(file4);

        Folder webFolder = new Folder("web");
        webFolder.add(file5);
        webFolder.add(file6);

        // 构建树结构
        Folder root = new Folder("project");
        root.add(file1);
        root.add(srcFolder);
        root.add(resourcesFolder);
        root.add(webFolder);

        // 显示整个目录结构
        root.display("");

        System.out.println("\n项目总大小: " + root.getSize() + "KB");
    }
}

输出:

📁 project (48KB)
  📄 README.md (5KB)
  📁 src (23KB)
    📄 Main.java (15KB)
    📄 Utils.java (8KB)
  📁 resources (2KB)
    📄 config.yml (2KB)
  📁 web (18KB)
    📄 style.css (12KB)
    📄 index.html (6KB)

项目总大小: 48KB

实现示例:组织架构 ​

组件接口 ​

java
// 组织成员接口
public abstract class OrganizationComponent {
    protected String name;
    protected String description;

    public OrganizationComponent(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public abstract int getMemberCount();
    public abstract void display(int depth);

    public void add(OrganizationComponent component) {
        throw new UnsupportedOperationException();
    }

    public void remove(OrganizationComponent component) {
        throw new UnsupportedOperationException();
    }

    public String getName() { return name; }
    public String getDescription() { return description; }
}

叶子节点(员工) ​

java
// 员工
public class Employee extends OrganizationComponent {
    private final String position;
    private final double salary;

    public Employee(String name, String position, double salary) {
        super(name, name + " - " + position);
        this.position = position;
        this.salary = salary;
    }

    @Override
    public int getMemberCount() {
        return 1;
    }

    @Override
    public void display(int depth) {
        String indent = "  ".repeat(depth);
        System.out.println(indent + "👤 " + name + " (" + position + ")");
    }
}

容器节点(部门) ​

java
// 部门
public class Department extends OrganizationComponent {
    private final List<OrganizationComponent> members = new ArrayList<>();

    public Department(String name, String description) {
        super(name, description);
    }

    @Override
    public void add(OrganizationComponent component) {
        members.add(component);
    }

    @Override
    public void remove(OrganizationComponent component) {
        members.remove(component);
    }

    @Override
    public int getMemberCount() {
        int count = 0;
        for (OrganizationComponent member : members) {
            count += member.getMemberCount();
        }
        return count;
    }

    @Override
    public void display(int depth) {
        String indent = "  ".repeat(depth);
        System.out.println(indent + "🏢 " + name + " (共 " + getMemberCount() + " 人)");
        for (OrganizationComponent member : members) {
            member.display(depth + 1);
        }
    }
}

使用示例 ​

java
public class OrganizationDemo {
    public static void main(String[] args) {
        // 创建员工
        Employee emp1 = new Employee("张三", "高级工程师", 20000);
        Employee emp2 = new Employee("李四", "中级工程师", 15000);
        Employee emp3 = new Employee("王五", "初级工程师", 10000);
        Employee emp4 = new Employee("赵六", "产品经理", 18000);
        Employee emp5 = new Employee("钱七", "UI 设计师", 14000);

        // 创建部门
        Department devDept = new Department("研发部", "负责产品开发");
        devDept.add(emp1);
        devDept.add(emp2);
        devDept.add(emp3);

        Department productDept = new Department("产品部", "负责产品设计");
        productDept.add(emp4);
        productDept.add(emp5);

        // 创建公司
        Department company = new Department("科技公司", "示例公司");
        company.add(devDept);
        company.add(productDept);

        // 显示组织架构
        company.display(0);

        System.out.println("\n公司总人数: " + company.getMemberCount());
    }
}

JDK 中的组合模式 ​

java.awt.Container ​

java
// Container 可以包含 Component(包括其他 Container)
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("Click");

frame.add(panel);
panel.add(button);

java.util.Map / HashMap ​

java
// HashMap 的 putAll 可以看作组合操作
Map<String, Object> parent = new HashMap<>();
Map<String, Object> child = new HashMap<>();
parent.put("key1", "value1");
child.put("key2", "value2");

透明方式 vs 安全方式 ​

特性透明方式安全方式
接口定义Component 中定义所有操作Component 只定义叶子和容器共有的操作
叶子实现需要抛出异常不需要
一致性高较低
安全性较低高

优缺点 ​

优点:

  • 高层调用简单,一致性好
  • 符合开闭原则,新增节点容易
  • 表示复杂的树形结构

缺点:

  • 叶子和容器的区别变得模糊
  • 新增组件类型可能需要修改抽象类

适用场景 ​

  • 需要表示对象的部分-整体层次结构
  • 希望统一对待单个对象和组合对象
  • 文件系统、组织架构、菜单系统等树形结构