设计模式之:组合模式

文章目录

什么是组合模式?

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性。

核心思想

组合模式的核心思想是:用树形结构来表示"部分-整体"的层次结构,让客户端可以统一地处理单个对象和对象组合。

生活中的组合模式

想象一下公司的组织架构:

  • 公司有各个部门
  • 部门下面有团队
  • 团队里面有员工
  • 无论是单个员工还是整个部门,都可以执行"工作"这个操作

这就是组合模式的思想!

模式结构

组合模式包含三个核心角色:

  1. 组件(Component):定义叶子和容器的共同接口
  2. 叶子(Leaf):表示树中的叶子节点,没有子节点
  3. 容器(Composite):表示树中的容器节点,有子节点

基础示例:文件系统

1. 组件接口

java 复制代码
/**
 * 文件系统组件接口 - 组件
 * 定义文件和文件夹的共同操作
 */
public interface FileSystemComponent {
    /**
     * 显示组件信息
     */
    void display();
    
    /**
     * 获取组件名称
     */
    String getName();
    
    /**
     * 获取组件大小
     */
    long getSize();
    
    /**
     * 获取组件类型
     */
    String getType();
    
    /**
     * 是否是文件
     */
    boolean isFile();
    
    /**
     * 是否是文件夹
     */
    boolean isDirectory();
    
    /**
     * 获取完整路径
     */
    default String getFullPath() {
        return getName();
    }
}

2. 叶子节点 - 文件

java 复制代码
/**
 * 文件类 - 叶子节点
 * 表示文件系统中的文件
 */
public class File implements FileSystemComponent {
    private final String name;
    private final long size;
    private final String extension;
    
    public File(String name, long size) {
        this.name = name;
        this.size = size;
        this.extension = extractExtension(name);
        System.out.println("📄 创建文件: " + name);
    }
    
    @Override
    public void display() {
        System.out.printf("📄 %s (%s) - %d bytes%n", name, extension, size);
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public long getSize() {
        return size;
    }
    
    @Override
    public String getType() {
        return "文件";
    }
    
    @Override
    public boolean isFile() {
        return true;
    }
    
    @Override
    public boolean isDirectory() {
        return false;
    }
    
    /**
     * 获取文件扩展名
     */
    private String extractExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        return lastDot > 0 ? filename.substring(lastDot + 1) : "无";
    }
    
    /**
     * 获取文件扩展名
     */
    public String getExtension() {
        return extension;
    }
    
    @Override
    public String toString() {
        return String.format("File{name='%s', size=%d, extension='%s'}", 
                           name, size, extension);
    }
}

3. 容器节点 - 文件夹

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 文件夹类 - 容器节点
 * 表示文件系统中的文件夹,可以包含文件和其他文件夹
 */
public class Directory implements FileSystemComponent {
    private final String name;
    private final List<FileSystemComponent> children;
    private Directory parent;
    
    public Directory(String name) {
        this.name = name;
        this.children = new ArrayList<>();
        this.parent = null;
        System.out.println("📁 创建文件夹: " + name);
    }
    
    @Override
    public void display() {
        System.out.printf("📁 %s/ [%d 个项目]%n", name, children.size());
        
        // 递归显示所有子组件
        for (FileSystemComponent child : children) {
            System.out.print("  ");
            child.display();
        }
    }
    
    /**
     * 添加子组件
     */
    public void addComponent(FileSystemComponent component) {
        if (component instanceof Directory) {
            ((Directory) component).setParent(this);
        }
        children.add(component);
        System.out.printf("➕ 添加 %s 到 %s%n", component.getName(), name);
    }
    
    /**
     * 移除子组件
     */
    public void removeComponent(FileSystemComponent component) {
        children.remove(component);
        System.out.printf("➖ 从 %s 移除 %s%n", name, component.getName());
    }
    
    /**
     * 获取子组件
     */
    public List<FileSystemComponent> getChildren() {
        return new ArrayList<>(children);
    }
    
    /**
     * 根据名称查找组件
     */
    public FileSystemComponent findComponent(String name) {
        for (FileSystemComponent child : children) {
            if (child.getName().equals(name)) {
                return child;
            }
            if (child instanceof Directory) {
                FileSystemComponent found = ((Directory) child).findComponent(name);
                if (found != null) {
                    return found;
                }
            }
        }
        return null;
    }
    
    /**
     * 获取文件夹大小(递归计算所有子组件大小)
     */
    @Override
    public long getSize() {
        long totalSize = 0;
        for (FileSystemComponent child : children) {
            totalSize += child.getSize();
        }
        return totalSize;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getType() {
        return "文件夹";
    }
    
    @Override
    public boolean isFile() {
        return false;
    }
    
    @Override
    public boolean isDirectory() {
        return true;
    }
    
    @Override
    public String getFullPath() {
        if (parent != null) {
            return parent.getFullPath() + "/" + name;
        }
        return name;
    }
    
    /**
     * 设置父文件夹
     */
    public void setParent(Directory parent) {
        this.parent = parent;
    }
    
    /**
     * 获取父文件夹
     */
    public Directory getParent() {
        return parent;
    }
    
    /**
     * 获取文件夹深度
     */
    public int getDepth() {
        if (parent == null) {
            return 0;
        }
        return parent.getDepth() + 1;
    }
    
    @Override
    public String toString() {
        return String.format("Directory{name='%s', children=%d, size=%d}", 
                           name, children.size(), getSize());
    }
}

4. 客户端使用

java 复制代码
/**
 * 文件系统客户端
 */
public class FileSystemClient {
    public static void main(String[] args) {
        System.out.println("=== 组合模式演示 - 文件系统 ===\n");
        
        // 创建文件系统结构
        Directory root = createFileSystem();
        
        // 演示1:显示整个文件系统
        demonstrateDisplay(root);
        
        // 演示2:计算大小
        demonstrateSizeCalculation(root);
        
        // 演示3:搜索文件
        demonstrateSearch(root);
        
        // 演示4:统一处理
        demonstrateUniformTreatment();
    }
    
    /**
     * 创建示例文件系统
     */
    private static Directory createFileSystem() {
        System.out.println("🛠️ 创建文件系统结构...");
        
        // 根目录
        Directory root = new Directory("根目录");
        
        // 文档文件夹
        Directory documents = new Directory("文档");
        documents.addComponent(new File("简历.pdf", 2048));
        documents.addComponent(new File("报告.docx", 4096));
        
        // 图片文件夹
        Directory pictures = new Directory("图片");
        pictures.addComponent(new File("照片1.jpg", 1024000));
        pictures.addComponent(new File("照片2.png", 2048000));
        
        // 在图片文件夹中创建子文件夹
        Directory vacation = new Directory("假期");
        vacation.addComponent(new File("海滩.jpg", 3072000));
        vacation.addComponent(new File("山峰.png", 4096000));
        pictures.addComponent(vacation);
        
        // 程序文件夹
        Directory programs = new Directory("程序");
        programs.addComponent(new File("app.exe", 5120000));
        programs.addComponent(new File("setup.msi", 10240000));
        
        // 将文件夹添加到根目录
        root.addComponent(documents);
        root.addComponent(pictures);
        root.addComponent(programs);
        root.addComponent(new File("说明.txt", 1024));
        
        System.out.println("✅ 文件系统创建完成\n");
        return root;
    }
    
    /**
     * 演示显示功能
     */
    private static void demonstrateDisplay(Directory root) {
        System.out.println("1. 文件系统结构显示:");
        System.out.println("=" .repeat(50));
        root.display();
    }
    
    /**
     * 演示大小计算
     */
    private static void demonstrateSizeCalculation(Directory root) {
        System.out.println("\n2. 文件大小计算:");
        System.out.println("-".repeat(40));
        
        // 计算整个文件系统大小
        System.out.printf("📊 整个文件系统大小: %,.0f KB%n", root.getSize() / 1024.0);
        
        // 计算各个文件夹大小
        for (FileSystemComponent child : root.getChildren()) {
            System.out.printf("📁 %s 大小: %,.0f KB%n", 
                child.getName(), child.getSize() / 1024.0);
        }
    }
    
    /**
     * 演示搜索功能
     */
    private static void demonstrateSearch(Directory root) {
        System.out.println("\n3. 文件搜索演示:");
        System.out.println("-".repeat(40));
        
        // 搜索文件
        String[] searchTargets = {"简历.pdf", "海滩.jpg", "不存在的文件.xyz"};
        
        for (String target : searchTargets) {
            FileSystemComponent found = root.findComponent(target);
            if (found != null) {
                System.out.printf("✅ 找到文件: %s (%s)%n", 
                    found.getName(), found.getFullPath());
            } else {
                System.out.printf("❌ 未找到文件: %s%n", target);
            }
        }
    }
    
    /**
     * 演示统一处理
     */
    private static void demonstrateUniformTreatment() {
        System.out.println("\n4. 统一处理演示:");
        System.out.println("-".repeat(40));
        
        // 创建混合列表(文件和文件夹)
        List<FileSystemComponent> components = new ArrayList<>();
        components.add(new File("单个文件.txt", 512));
        components.add(new Directory("单个文件夹"));
        
        // 统一处理 - 客户端不需要知道具体类型
        System.out.println("统一处理文件和文件夹:");
        for (FileSystemComponent component : components) {
            System.out.printf("🔹 %s: %s (大小: %d bytes)%n", 
                component.getType(), component.getName(), component.getSize());
        }
    }
}

完整示例:组织架构系统

让我们通过一个更复杂的组织架构系统来深入理解组合模式。

1. 组织组件接口

java 复制代码
import java.util.List;

/**
 * 组织组件接口 - 组件
 * 定义员工和部门的共同操作
 */
public interface OrganizationComponent {
    /**
     * 显示组件信息
     */
    void display();
    
    /**
     * 获取名称
     */
    String getName();
    
    /**
     * 获取职责描述
     */
    String getDescription();
    
    /**
     * 计算薪资成本
     */
    double calculateCost();
    
    /**
     * 获取成员数量
     */
    int getMemberCount();
    
    /**
     * 添加子组件(部门专用)
     */
    default void addComponent(OrganizationComponent component) {
        throw new UnsupportedOperationException("不支持添加操作");
    }
    
    /**
     * 移除子组件(部门专用)
     */
    default void removeComponent(OrganizationComponent component) {
        throw new UnsupportedOperationException("不支持移除操作");
    }
    
    /**
     * 获取子组件(部门专用)
     */
    default List<OrganizationComponent> getChildren() {
        throw new UnsupportedOperationException("不支持获取子组件操作");
    }
    
    /**
     * 是否是员工
     */
    boolean isEmployee();
    
    /**
     * 是否是部门
     */
    boolean isDepartment();
}

2. 叶子节点 - 员工

java 复制代码
/**
 * 员工类 - 叶子节点
 * 表示组织中的员工
 */
public class Employee implements OrganizationComponent {
    private final String name;
    private final String position;
    private final double salary;
    private final String department;
    
    public Employee(String name, String position, double salary, String department) {
        this.name = name;
        this.position = position;
        this.salary = salary;
        this.department = department;
        System.out.printf("👤 创建员工: %s (%s)%n", name, position);
    }
    
    @Override
    public void display() {
        System.out.printf("👤 %s - %s [部门: %s, 薪资: ¥%,.0f]%n", 
                         name, position, department, salary);
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getDescription() {
        return String.format("员工 %s - %s", name, position);
    }
    
    @Override
    public double calculateCost() {
        return salary;
    }
    
    @Override
    public int getMemberCount() {
        return 1; // 单个员工就是1个成员
    }
    
    @Override
    public boolean isEmployee() {
        return true;
    }
    
    @Override
    public boolean isDepartment() {
        return false;
    }
    
    // Getter方法
    public String getPosition() {
        return position;
    }
    
    public double getSalary() {
        return salary;
    }
    
    public String getDepartment() {
        return department;
    }
    
    /**
     * 员工工作
     */
    public void work() {
        System.out.printf("💼 %s 正在工作...%n", name);
    }
    
    @Override
    public String toString() {
        return String.format("Employee{name='%s', position='%s', salary=%.0f}", 
                           name, position, salary);
    }
}

3. 容器节点 - 部门

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 部门类 - 容器节点
 * 表示组织中的部门,可以包含员工和其他部门
 */
public class Department implements OrganizationComponent {
    private final String name;
    private final String description;
    private final List<OrganizationComponent> children;
    private Department parent;
    
    public Department(String name, String description) {
        this.name = name;
        this.description = description;
        this.children = new ArrayList<>();
        this.parent = null;
        System.out.printf("🏢 创建部门: %s%n", name);
    }
    
    @Override
    public void display() {
        System.out.printf("🏢 %s - %s [成员: %d 人, 成本: ¥%,.0f]%n", 
                         name, description, getMemberCount(), calculateCost());
        
        // 递归显示所有子组件
        for (OrganizationComponent child : children) {
            System.out.print("  ");
            child.display();
        }
    }
    
    @Override
    public void addComponent(OrganizationComponent component) {
        if (component instanceof Department) {
            ((Department) component).setParent(this);
        }
        children.add(component);
        System.out.printf("➕ 添加 %s 到 %s%n", component.getName(), name);
    }
    
    @Override
    public void removeComponent(OrganizationComponent component) {
        children.remove(component);
        System.out.printf("➖ 从 %s 移除 %s%n", name, component.getName());
    }
    
    @Override
    public List<OrganizationComponent> getChildren() {
        return new ArrayList<>(children);
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getDescription() {
        return description;
    }
    
    /**
     * 计算部门总成本(递归计算所有子组件成本)
     */
    @Override
    public double calculateCost() {
        double totalCost = 0;
        for (OrganizationComponent child : children) {
            totalCost += child.calculateCost();
        }
        return totalCost;
    }
    
    /**
     * 获取部门总人数(递归计算所有子组件人数)
     */
    @Override
    public int getMemberCount() {
        int totalCount = 0;
        for (OrganizationComponent child : children) {
            totalCount += child.getMemberCount();
        }
        return totalCount;
    }
    
    @Override
    public boolean isEmployee() {
        return false;
    }
    
    @Override
    public boolean isDepartment() {
        return true;
    }
    
    /**
     * 设置父部门
     */
    public void setParent(Department parent) {
        this.parent = parent;
    }
    
    /**
     * 获取父部门
     */
    public Department getParent() {
        return parent;
    }
    
    /**
     * 根据名称查找组件
     */
    public OrganizationComponent findComponent(String name) {
        for (OrganizationComponent child : children) {
            if (child.getName().equals(name)) {
                return child;
            }
            if (child instanceof Department) {
                OrganizationComponent found = ((Department) child).findComponent(name);
                if (found != null) {
                    return found;
                }
            }
        }
        return null;
    }
    
    /**
     * 获取部门层级
     */
    public int getLevel() {
        if (parent == null) {
            return 0;
        }
        return parent.getLevel() + 1;
    }
    
    /**
     * 获取所有员工
     */
    public List<Employee> getAllEmployees() {
        List<Employee> employees = new ArrayList<>();
        for (OrganizationComponent child : children) {
            if (child instanceof Employee) {
                employees.add((Employee) child);
            } else if (child instanceof Department) {
                employees.addAll(((Department) child).getAllEmployees());
            }
        }
        return employees;
    }
    
    /**
     * 部门运营
     */
    public void operate() {
        System.out.printf("🏢 %s 部门正在运营...%n", name);
        for (OrganizationComponent child : children) {
            if (child instanceof Employee) {
                ((Employee) child).work();
            } else if (child instanceof Department) {
                ((Department) child).operate();
            }
        }
    }
    
    @Override
    public String toString() {
        return String.format("Department{name='%s', members=%d, cost=%.0f}", 
                           name, getMemberCount(), calculateCost());
    }
}

4. 组织架构客户端

java 复制代码
/**
 * 组织架构客户端
 */
public class OrganizationClient {
    public static void main(String[] args) {
        System.out.println("=== 组合模式演示 - 组织架构系统 ===\n");
        
        // 创建公司组织架构
        Department company = createCompanyStructure();
        
        // 演示1:显示组织架构
        demonstrateOrganizationDisplay(company);
        
        // 演示2:成本计算
        demonstrateCostCalculation(company);
        
        // 演示3:统一操作
        demonstrateUniformOperations(company);
        
        // 演示4:高级功能
        demonstrateAdvancedFeatures(company);
    }
    
    /**
     * 创建公司组织架构
     */
    private static Department createCompanyStructure() {
        System.out.println("🏗️ 构建公司组织架构...");
        
        // 创建公司
        Department company = new Department("科技有限公司", "总部");
        
        // 技术部门
        Department techDepartment = new Department("技术部", "产品研发");
        techDepartment.addComponent(new Employee("张三", "Java工程师", 25000, "技术部"));
        techDepartment.addComponent(new Employee("李四", "前端工程师", 22000, "技术部"));
        techDepartment.addComponent(new Employee("王五", "架构师", 35000, "技术部"));
        
        // 后端开发组
        Department backendTeam = new Department("后端开发组", "服务器端开发");
        backendTeam.addComponent(new Employee("赵六", "高级Java工程师", 28000, "后端开发组"));
        backendTeam.addComponent(new Employee("钱七", "Python工程师", 26000, "后端开发组"));
        techDepartment.addComponent(backendTeam);
        
        // 市场部门
        Department marketingDepartment = new Department("市场部", "市场推广和销售");
        marketingDepartment.addComponent(new Employee("孙八", "市场经理", 30000, "市场部"));
        marketingDepartment.addComponent(new Employee("周九", "销售代表", 18000, "市场部"));
        marketingDepartment.addComponent(new Employee("吴十", "市场专员", 15000, "市场部"));
        
        // 人事部门
        Department hrDepartment = new Department("人事部", "人力资源管理和招聘");
        hrDepartment.addComponent(new Employee("郑十一", "HR经理", 28000, "人事部"));
        hrDepartment.addComponent(new Employee("王十二", "招聘专员", 16000, "人事部"));
        
        // 构建组织架构
        company.addComponent(techDepartment);
        company.addComponent(marketingDepartment);
        company.addComponent(hrDepartment);
        
        System.out.println("✅ 公司组织架构构建完成\n");
        return company;
    }
    
    /**
     * 演示组织架构显示
     */
    private static void demonstrateOrganizationDisplay(Department company) {
        System.out.println("1. 公司组织架构:");
        System.out.println("=" .repeat(60));
        company.display();
    }
    
    /**
     * 演示成本计算
     */
    private static void demonstrateCostCalculation(Department company) {
        System.out.println("\n2. 组织成本分析:");
        System.out.println("-".repeat(40));
        
        System.out.printf("💰 公司总薪资成本: ¥%,.0f/月%n", company.calculateCost());
        System.out.printf("👥 公司总人数: %d 人%n", company.getMemberCount());
        System.out.printf("📊 人均成本: ¥%,.0f/月%n", 
            company.calculateCost() / company.getMemberCount());
        
        // 各部门成本分析
        System.out.println("\n📈 各部门成本分析:");
        for (OrganizationComponent dept : company.getChildren()) {
            System.out.printf("🏢 %s: ¥%,.0f/月 (%d 人)%n", 
                dept.getName(), dept.calculateCost(), dept.getMemberCount());
        }
    }
    
    /**
     * 演示统一操作
     */
    private static void demonstrateUniformOperations(Department company) {
        System.out.println("\n3. 统一操作演示:");
        System.out.println("-".repeat(40));
        
        // 创建混合列表(员工和部门)
        List<OrganizationComponent> components = new ArrayList<>();
        components.add(new Employee("临时员工", "实习生", 8000, "临时"));
        components.add(new Department("临时部门", "项目组"));
        
        System.out.println("统一处理员工和部门:");
        for (OrganizationComponent component : components) {
            System.out.printf("🔹 %s: %s (成本: ¥%,.0f, 成员: %d)%n", 
                component.isEmployee() ? "员工" : "部门",
                component.getName(), 
                component.calculateCost(),
                component.getMemberCount());
        }
        
        System.out.println("\n🏢 公司运营模拟:");
        company.operate();
    }
    
    /**
     * 演示高级功能
     */
    private static void demonstrateAdvancedFeatures(Department company) {
        System.out.println("\n4. 高级功能演示:");
        System.out.println("-".repeat(40));
        
        // 搜索功能
        System.out.println("🔍 员工搜索:");
        String[] searchNames = {"张三", "赵六", "不存在的员工"};
        for (String name : searchNames) {
            OrganizationComponent found = company.findComponent(name);
            if (found != null) {
                System.out.printf("✅ 找到: %s%n", found.getDescription());
            } else {
                System.out.printf("❌ 未找到: %s%n", name);
            }
        }
        
        // 获取所有员工
        System.out.println("\n📋 公司所有员工列表:");
        List<Employee> allEmployees = company.getAllEmployees();
        for (Employee employee : allEmployees) {
            System.out.printf("👤 %s - %s (¥%,.0f)%n", 
                employee.getName(), employee.getPosition(), employee.getSalary());
        }
        
        // 动态调整组织架构
        System.out.println("\n🔄 动态调整组织架构:");
        Department newTeam = new Department("新产品组", "创新产品研发");
        newTeam.addComponent(new Employee("新员工", "产品经理", 32000, "新产品组"));
        company.addComponent(newTeam);
        
        System.out.printf("调整后总人数: %d 人%n", company.getMemberCount());
        System.out.printf("调整后总成本: ¥%,.0f/月%n", company.calculateCost());
    }
}

实际应用示例:GUI组件系统

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * GUI组件系统 - 组合模式实际应用
 */
public class GUIComponentSystem {
    public static void main(String[] args) {
        System.out.println("=== 组合模式演示 - GUI组件系统 ===\n");
        
        // 创建GUI界面
        Window mainWindow = createGUI();
        
        // 显示GUI结构
        mainWindow.display();
        
        // 演示渲染操作
        System.out.println("\n🎨 渲染GUI界面:");
        mainWindow.render();
    }
    
    private static Window createGUI() {
        System.out.println("🖼️ 创建GUI界面...");
        
        // 创建主窗口
        Window mainWindow = new Window("主窗口", 800, 600);
        
        // 创建工具栏
        Panel toolbar = new Panel("工具栏");
        toolbar.addComponent(new Button("文件"));
        toolbar.addComponent(new Button("编辑"));
        toolbar.addComponent(new Button("视图"));
        
        // 创建侧边栏
        Panel sidebar = new Panel("侧边栏");
        sidebar.addComponent(new Button("导航"));
        sidebar.addComponent(new Button("书签"));
        sidebar.addComponent(new Button("历史"));
        
        // 创建内容区域
        Panel contentArea = new Panel("内容区域");
        contentArea.addComponent(new TextField("搜索框", 200));
        
        // 创建嵌套面板
        Panel settingsPanel = new Panel("设置面板");
        settingsPanel.addComponent(new Checkbox("自动保存"));
        settingsPanel.addComponent(new Checkbox("显示行号"));
        contentArea.addComponent(settingsPanel);
        
        // 构建界面
        mainWindow.addComponent(toolbar);
        mainWindow.addComponent(sidebar);
        mainWindow.addComponent(contentArea);
        
        return mainWindow;
    }
}

/**
 * GUI组件接口
 */
interface GUIComponent {
    void display();
    void render();
    String getName();
}

/**
 * 窗口 - 容器组件
 */
class Window implements GUIComponent {
    private String name;
    private int width;
    private int height;
    private List<GUIComponent> children;
    
    public Window(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
        this.children = new ArrayList<>();
    }
    
    public void addComponent(GUIComponent component) {
        children.add(component);
    }
    
    @Override
    public void display() {
        System.out.printf("🪟 %s [%dx%d]%n", name, width, height);
        for (GUIComponent child : children) {
            System.out.print("  ");
            child.display();
        }
    }
    
    @Override
    public void render() {
        System.out.printf("🎨 渲染窗口: %s%n", name);
        for (GUIComponent child : children) {
            child.render();
        }
    }
    
    @Override
    public String getName() {
        return name;
    }
}

/**
 * 面板 - 容器组件
 */
class Panel implements GUIComponent {
    private String name;
    private List<GUIComponent> children;
    
    public Panel(String name) {
        this.name = name;
        this.children = new ArrayList<>();
    }
    
    public void addComponent(GUIComponent component) {
        children.add(component);
    }
    
    @Override
    public void display() {
        System.out.printf("📦 面板: %s%n", name);
        for (GUIComponent child : children) {
            System.out.print("    ");
            child.display();
        }
    }
    
    @Override
    public void render() {
        System.out.printf("🎨 渲染面板: %s%n", name);
        for (GUIComponent child : children) {
            child.render();
        }
    }
    
    @Override
    public String getName() {
        return name;
    }
}

/**
 * 按钮 - 叶子组件
 */
class Button implements GUIComponent {
    private String label;
    
    public Button(String label) {
        this.label = label;
    }
    
    @Override
    public void display() {
        System.out.printf("🔘 按钮: %s%n", label);
    }
    
    @Override
    public void render() {
        System.out.printf("🎨 渲染按钮: %s%n", label);
    }
    
    @Override
    public String getName() {
        return "按钮-" + label;
    }
}

/**
 * 文本框 - 叶子组件
 */
class TextField implements GUIComponent {
    private String placeholder;
    private int width;
    
    public TextField(String placeholder, int width) {
        this.placeholder = placeholder;
        this.width = width;
    }
    
    @Override
    public void display() {
        System.out.printf("📝 文本框: %s [宽度:%d]%n", placeholder, width);
    }
    
    @Override
    public void render() {
        System.out.printf("🎨 渲染文本框: %s%n", placeholder);
    }
    
    @Override
    public String getName() {
        return "文本框-" + placeholder;
    }
}

/**
 * 复选框 - 叶子组件
 */
class Checkbox implements GUIComponent {
    private String label;
    
    public Checkbox(String label) {
        this.label = label;
    }
    
    @Override
    public void display() {
        System.out.printf("☑️ 复选框: %s%n", label);
    }
    
    @Override
    public void render() {
        System.out.printf("🎨 渲染复选框: %s%n", label);
    }
    
    @Override
    public String getName() {
        return "复选框-" + label;
    }
}

组合模式的优点

1. 统一处理

java 复制代码
// 客户端可以统一处理单个对象和对象组合
// 不需要关心具体是叶子节点还是容器节点

2. 简化客户端代码

java 复制代码
// 客户端代码变得简单清晰
// 不需要写很多条件判断

3. 容易扩展

java 复制代码
// 可以很容易地添加新的组件类型
// 符合开闭原则

组合模式的缺点

1. 设计较复杂

java 复制代码
// 需要正确设计组件接口
// 要处理叶子节点和容器节点的差异

2. 类型安全检查

java 复制代码
// 在运行时才能发现某些操作不支持
// 比如在叶子节点上调用addComponent()

适用场景

  1. 需要表示对象的部分-整体层次结构时
  2. 希望客户端忽略组合对象与单个对象的差异时
  3. 处理树形结构数据时

最佳实践

1. 合理设计组件接口

java 复制代码
public interface Component {
    // 共同操作
    void operation();
    
    // 容器操作(提供默认实现)
    default void add(Component c) {
        throw new UnsupportedOperationException();
    }
}

2. 考虑透明性和安全性

java 复制代码
// 透明性:在接口中声明所有方法
// 安全性:叶子节点不实现容器相关方法

3. 实现迭代器支持

java 复制代码
// 可以为组合结构实现迭代器
// 方便遍历整个树形结构

组合模式 vs 其他模式

模式 目的 特点
组合模式 处理树形结构 统一处理单个对象和对象组合
装饰器模式 动态添加功能 包装对象,增强功能
迭代器模式 遍历集合 提供统一的遍历接口

总结

组合模式就像是"俄罗斯套娃",可以无限嵌套,但对外表现一致。

核心价值:

  • 统一处理单个对象和对象组合
  • 简化复杂树形结构的操作
  • 提高代码的灵活性和可扩展性

使用场景:

  • 需要处理树形结构数据时
  • 希望统一处理部分和整体时
  • 构建层次化系统时

简单记忆:

部分整体难区分?组合模式来解决!

树形结构轻松管,统一处理真方便。

掌握组合模式,能够让你优雅地处理复杂的层次结构,让代码更加清晰和灵活!

相关推荐
Hero | 柒6 小时前
设计模式之建造者模式
java·设计模式·1024程序员节
周杰伦_Jay8 小时前
【常用设计模式全解析】创建型模式(聚焦对象创建机制)、结构型模式(优化类与对象的组合关系)、行为型模式(规范对象间的交互行为)
设计模式·架构·开源·交互·1024程序员节
杯莫停丶10 小时前
设计模式之:外观模式
java·设计模式·外观模式
杯莫停丶11 小时前
设计模式之:命令模式
设计模式·命令模式·1024程序员节·活动勋章
电子科技圈13 小时前
芯科科技推出智能开发工具Simplicity Ecosystem软件开发套件开启物联网开发的新高度
mcu·物联网·设计模式·软件工程·软件构建·iot·1024程序员节
WaWaJie_Ngen14 小时前
【设计模式】组合模式(Composite)
设计模式·组合模式·1024程序员节
WaWaJie_Ngen16 小时前
【设计模式】装饰器模式(Decorator)
设计模式·装饰器模式
YuanlongWang16 小时前
C# 设计模式——观察者
windows·设计模式·c#
亿牛云爬虫专家16 小时前
中间件实现任务去重与精细化分发:设计模式与常见陷阱
设计模式·中间件·爬虫代理·数据抓取·商品信息·数据去重·电商搜索