模式定义

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

模式结构

graph TD A[Client] --> B((Component)) B --> C[Leaf] B --> D[Composite] D --> E[Operation] D --> F[Add/Remove]

实现方式

基础实现

定义组件接口,实现叶子节点和复合节点。

// 组件抽象类
public abstract class Employee {
    protected String name;
    protected String dept;
    protected int salary;
    
    public Employee(String name, String dept, int sal) {
        this.name = name;
        this.dept = dept;
        this.salary = sal;
    }
    
    public abstract void add(Employee e);
    public abstract void remove(Employee e);
    public abstract List getSubordinates();
    
    public String toString() {
        return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary + " ]");
    }
}

// 叶子节点 - 普通员工
import java.util.ArrayList;
import java.util.List;

public class Developer extends Employee {
    public Developer(String name, String dept, int salary) {
        super(name, dept, salary);
    }
    
    @Override
    public void add(Employee e) {
        // 叶子节点不支持添加操作
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void remove(Employee e) {
        // 叶子节点不支持删除操作
        throw new UnsupportedOperationException();
    }
    
    @Override
    public List getSubordinates() {
        // 叶子节点没有下属
        return new ArrayList<>();
    }
}

// 叶子节点 - 设计师
public class Designer extends Employee {
    public Designer(String name, String dept, int salary) {
        super(name, dept, salary);
    }
    
    @Override
    public void add(Employee e) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void remove(Employee e) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public List getSubordinates() {
        return new ArrayList<>();
    }
}

// 复合节点 - 管理者
import java.util.ArrayList;
import java.util.List;

public class Manager extends Employee {
    private List subordinates;
    
    public Manager(String name, String dept, int salary) {
        super(name, dept, salary);
        subordinates = new ArrayList<>();
    }
    
    @Override
    public void add(Employee e) {
        subordinates.add(e);
    }
    
    @Override
    public void remove(Employee e) {
        subordinates.remove(e);
    }
    
    @Override
    public List getSubordinates() {
        return subordinates;
    }
}

使用示例

import java.util.List;

public class CompositePatternDemo {
    public static void main(String[] args) {
        // 创建员工
        Employee CEO = new Manager("John", "CEO", 30000);
        
        Employee headSales = new Manager("Robert", "Head Sales", 20000);
        Employee headMarketing = new Manager("Michel", "Head Marketing", 20000);
        
        Employee clerk1 = new Developer("Laura", "Marketing", 10000);
        Employee clerk2 = new Developer("Bob", "Marketing", 10000);
        
        Employee salesExecutive1 = new Developer("Richard", "Sales", 10000);
        Employee salesExecutive2 = new Developer("Rob", "Sales", 10000);
        
        // 创建组织结构
        CEO.add(headSales);
        CEO.add(headMarketing);
        
        headSales.add(salesExecutive1);
        headSales.add(salesExecutive2);
        
        headMarketing.add(clerk1);
        headMarketing.add(clerk2);
        
        // 打印组织结构
        System.out.println(CEO);
        
        for (Employee headEmployee : CEO.getSubordinates()) {
            System.out.println(headEmployee);
            
            for (Employee employee : headEmployee.getSubordinates()) {
                System.out.println(employee);
            }
        }
    }
}

文件系统示例

使用组合模式实现简单的文件系统结构。

// 文件系统组件接口
public abstract class FileSystemComponent {
    protected String name;
    
    public FileSystemComponent(String name) {
        this.name = name;
    }
    
    public abstract void display(int depth);
    public abstract long getSize();
    
    // 默认实现,叶子节点不支持这些操作
    public void add(FileSystemComponent component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }
    
    public void remove(FileSystemComponent component) {
        throw new UnsupportedOperationException("不支持删除操作");
    }
    
    public FileSystemComponent getChild(int i) {
        throw new UnsupportedOperationException("不支持获取子节点操作");
    }
}

// 叶子节点 - 文件
public class File extends FileSystemComponent {
    private long size;
    
    public File(String name, long size) {
        super(name);
        this.size = size;
    }
    
    @Override
    public void display(int depth) {
        System.out.println(" ".repeat(depth) + "- File: " + name + " (" + size + " bytes)");
    }
    
    @Override
    public long getSize() {
        return size;
    }
}

// 复合节点 - 文件夹
import java.util.ArrayList;
import java.util.List;

public class Folder extends FileSystemComponent {
    private List 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 i) {
        return children.get(i);
    }
    
    @Override
    public void display(int depth) {
        System.out.println(" ".repeat(depth) + "+ Folder: " + name);
        for (FileSystemComponent component : children) {
            component.display(depth + 2);
        }
    }
    
    @Override
    public long getSize() {
        long totalSize = 0;
        for (FileSystemComponent component : children) {
            totalSize += component.getSize();
        }
        return totalSize;
    }
}

// 客户端使用
public class FileSystemDemo {
    public static void main(String[] args) {
        // 创建文件系统结构
        Folder root = new Folder("root");
        
        Folder documents = new Folder("Documents");
        Folder pictures = new Folder("Pictures");
        Folder music = new Folder("Music");
        
        File doc1 = new File("resume.doc", 1024);
        File doc2 = new File("reference.pdf", 2048);
        
        File pic1 = new File("photo1.jpg", 204800);
        File pic2 = new File("photo2.jpg", 307200);
        
        File song1 = new File("song1.mp3", 5120000);
        File song2 = new File("song2.mp3", 4096000);
        
        // 构建目录结构
        root.add(documents);
        root.add(pictures);
        root.add(music);
        
        documents.add(doc1);
        documents.add(doc2);
        
        pictures.add(pic1);
        pictures.add(pic2);
        
        music.add(song1);
        music.add(song2);
        
        // 显示文件系统结构
        root.display(0);
        
        System.out.println("\n总大小: " + root.getSize() + " bytes");
    }
}

经典案例

AWT/Swing GUI组件

Java AWT和Swing中的组件容器是组合模式的经典应用。

// Swing中的组合模式示例
import javax.swing.*;
import java.awt.*;

public class SwingCompositeExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("组合模式示例");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        
        // JPanel是容器组件(Composite)
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        
        // JLabel是叶子组件(Leaf)
        JLabel label = new JLabel("这是一个标签", JLabel.CENTER);
        
        // JButton是叶子组件(Leaf)
        JButton button1 = new JButton("按钮1");
        JButton button2 = new JButton("按钮2");
        
        // JPanel可以包含其他组件
        panel.add(label, BorderLayout.CENTER);
        panel.add(button1, BorderLayout.NORTH);
        panel.add(button2, BorderLayout.SOUTH);
        
        // JFrame也可以包含JPanel
        frame.add(panel);
        frame.setVisible(true);
    }
}

适用场景

优缺点

优点

  • 高层模块调用简单:客户端可以一致地使用组合结构和单个对象
  • 节点自由增加:容易在组合体中加入新的构件
  • 定义了包含基本对象和组合对象的类层次结构:基本对象可以被组合成更复杂的组合对象
  • 简化客户端代码:客户端可以一致地使用组合对象和单个对象
  • 更容易在组合体内加入新构件:客户端不会因为加入了新的构件而更改代码

缺点

  • 很难限制容器中的构件类型
  • 使设计变得更加抽象,不容易理解
  • 不容易限制容器中的构件类型:有时候系统需要依赖于构件的类型
  • 用继承的方法来增加新的行为很困难:继承会使系统中类的数量大量增加