【设计模式手册010】组合模式 - 树形结构的优雅处理

设计模式手册010:组合模式 - 树形结构的优雅处理

本文是「设计模式手册」系列第010篇,我们将深入探讨组合模式,这种模式用树形结构来表示"部分-整体"的层次结构,让客户端可以统一地处理单个对象和对象组合。

1. 场景:我们为何需要组合模式?

在软件开发中,我们经常遇到需要处理树形结构数据的场景:

  • 文件系统:目录包含文件和子目录
  • 组织架构:部门包含员工和子部门
  • 菜单系统:菜单包含菜单项和子菜单
  • UI组件:容器组件包含叶子组件和其他容器
  • 商品分类:分类包含商品和子分类

传统做法的困境

java 复制代码
// 不同的类需要不同的处理方式
public class File {
    private String name;
    private long size;
    
    public File(String name, long size) {
        this.name = name;
        this.size = size;
    }
    
    public String getName() { return name; }
    public long getSize() { return size; }
    public void display() {
        System.out.println("文件: " + name + " (" + size + " bytes)");
    }
}

public class Directory {
    private String name;
    private List<Object> children; // 混合类型!
    
    public Directory(String name) {
        this.name = name;
        this.children = new ArrayList<>();
    }
    
    public void add(Object child) {
        children.add(child);
    }
    
    public void display() {
        System.out.println("目录: " + name);
        for (Object child : children) {
            if (child instanceof File) {
                ((File) child).display();
            } else if (child instanceof Directory) {
                ((Directory) child).display();
            }
        }
    }
    
    public long getSize() {
        long totalSize = 0;
        for (Object child : children) {
            if (child instanceof File) {
                totalSize += ((File) child).getSize();
            } else if (child instanceof Directory) {
                totalSize += ((Directory) child).getSize();
            }
        }
        return totalSize;
    }
}

// 问题:客户端需要区分不同类型
public class FileSystemClient {
    public static void main(String[] args) {
        Directory root = new Directory("root");
        File file1 = new File("readme.txt", 1024);
        Directory subDir = new Directory("documents");
        File file2 = new File("report.doc", 2048);
        
        root.add(file1);
        root.add(subDir);
        subDir.add(file2);
        
        // 显示时需要类型判断
        root.display();
        
        // 计算大小需要类型判断
        System.out.println("总大小: " + root.getSize());
    }
}

这种实现的痛点

  • 类型判断:需要不断检查对象类型
  • 代码重复:相同的遍历逻辑在不同方法中重复
  • 违反开闭原则:新增类型需要修改所有处理代码
  • 客户端复杂:客户端需要了解具体类型

2. 组合模式:定义与本质

2.1 模式定义

组合模式(Composite Pattern):将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

2.2 核心角色

java 复制代码
// 组件接口:定义叶子和容器的共同接口
public interface FileSystemComponent {
    String getName();
    long getSize();
    void display();
    void add(FileSystemComponent component);
    void remove(FileSystemComponent component);
    FileSystemComponent getChild(int index);
}

// 叶子组件:表示叶子节点,没有子节点
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() {
        System.out.println("文件: " + name + " (" + size + " bytes)");
    }
    
    // 叶子节点不支持这些操作,可以抛出异常或忽略
    @Override
    public void add(FileSystemComponent component) {
        throw new UnsupportedOperationException("文件不支持添加子组件");
    }
    
    @Override
    public void remove(FileSystemComponent component) {
        throw new UnsupportedOperationException("文件不支持删除子组件");
    }
    
    @Override
    public FileSystemComponent getChild(int index) {
        throw new UnsupportedOperationException("文件没有子组件");
    }
}

// 容器组件:表示容器节点,可以包含子节点
public class Directory implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> children;
    
    public Directory(String name) {
        this.name = name;
        this.children = new ArrayList<>();
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public long getSize() {
        long totalSize = 0;
        for (FileSystemComponent child : children) {
            totalSize += child.getSize();
        }
        return totalSize;
    }
    
    @Override
    public void display() {
        System.out.println("目录: " + name);
        for (FileSystemComponent child : children) {
            child.display(); // 递归调用
        }
    }
    
    @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;
    }
    
    // 容器特有的方法
    public int getChildCount() {
        return children.size();
    }
    
    public List<FileSystemComponent> getChildren() {
        return new ArrayList<>(children);
    }
}

3. 深入理解:组合模式的多维视角

3.1 第一重:透明式 vs 安全式

透明式组合模式
java 复制代码
// 透明式:在组件接口中声明所有方法,包括管理子组件的方法
public interface Component {
    void operation();
    void add(Component component);
    void remove(Component component);
    Component getChild(int index);
}

// 叶子节点需要实现所有方法,包括不支持的方法
public class Leaf implements Component {
    @Override
    public void operation() {
        System.out.println("叶子操作");
    }
    
    @Override
    public void add(Component component) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public Component getChild(int index) {
        throw new UnsupportedOperationException();
    }
}
安全式组合模式
java 复制代码
// 安全式:在组件接口中只声明公共方法,管理子组件的方法在容器接口中声明
public interface Component {
    void operation();
}

public interface Composite extends Component {
    void add(Component component);
    void remove(Component component);
    Component getChild(int index);
}

// 叶子节点只需要实现操作方法
public class Leaf implements Component {
    @Override
    public void operation() {
        System.out.println("叶子操作");
    }
}

// 容器节点实现复合接口
public class ConcreteComposite implements Composite {
    private List<Component> children = new ArrayList<>();
    
    @Override
    public void operation() {
        System.out.println("容器操作");
        for (Component child : children) {
            child.operation();
        }
    }
    
    @Override
    public void add(Component component) {
        children.add(component);
    }
    
    @Override
    public void remove(Component component) {
        children.remove(component);
    }
    
    @Override
    public Component getChild(int index) {
        return children.get(index);
    }
}

3.2 第二重:递归组合的力量

java 复制代码
// 利用递归处理复杂树形结构
public class FileSystemComponent {
    // ... 基础方法
    
    // 递归查找
    public FileSystemComponent find(String name) {
        if (this.getName().equals(name)) {
            return this;
        }
        
        if (this instanceof Directory) {
            Directory dir = (Directory) this;
            for (FileSystemComponent child : dir.getChildren()) {
                FileSystemComponent result = child.find(name);
                if (result != null) {
                    return result;
                }
            }
        }
        
        return null;
    }
    
    // 递归统计
    public int countFiles() {
        if (this instanceof File) {
            return 1;
        }
        
        int count = 0;
        if (this instanceof Directory) {
            Directory dir = (Directory) this;
            for (FileSystemComponent child : dir.getChildren()) {
                count += child.countFiles();
            }
        }
        return count;
    }
}

3.3 第三重:组合迭代器

java 复制代码
// 为组合结构提供迭代器
public interface ComponentIterator extends Iterator<FileSystemComponent> {
    boolean hasNext();
    FileSystemComponent next();
}

public class DepthFirstIterator implements ComponentIterator {
    private Stack<FileSystemComponent> stack = new Stack<>();
    
    public DepthFirstIterator(FileSystemComponent root) {
        stack.push(root);
    }
    
    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }
    
    @Override
    public FileSystemComponent next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        
        FileSystemComponent component = stack.pop();
        if (component instanceof Directory) {
            Directory dir = (Directory) component;
            // 逆序压栈,保证正序遍历
            for (int i = dir.getChildCount() - 1; i >= 0; i--) {
                stack.push(dir.getChild(i));
            }
        }
        return component;
    }
}

4. 实战案例:完整的组织架构系统

java 复制代码
// 组织组件接口
public interface OrganizationComponent {
    String getName();
    String getPosition();
    double getSalary();
    void display();
    void add(OrganizationComponent component);
    void remove(OrganizationComponent component);
    OrganizationComponent getChild(int index);
    
    // 业务方法
    double calculateTotalSalary();
    int countEmployees();
    void printOrganizationChart();
}

// 员工类 - 叶子节点
public class Employee implements OrganizationComponent {
    private String name;
    private String position;
    private double salary;
    
    public Employee(String name, String position, double salary) {
        this.name = name;
        this.position = position;
        this.salary = salary;
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getPosition() {
        return position;
    }
    
    @Override
    public double getSalary() {
        return salary;
    }
    
    @Override
    public void display() {
        System.out.println("员工: " + name + " - " + position + " - 薪资: " + salary);
    }
    
    @Override
    public double calculateTotalSalary() {
        return salary;
    }
    
    @Override
    public int countEmployees() {
        return 1;
    }
    
    @Override
    public void printOrganizationChart() {
        System.out.println("  └── " + name + " (" + position + ")");
    }
    
    @Override
    public void add(OrganizationComponent component) {
        throw new UnsupportedOperationException("员工不能有下属");
    }
    
    @Override
    public void remove(OrganizationComponent component) {
        throw new UnsupportedOperationException("员工不能删除下属");
    }
    
    @Override
    public OrganizationComponent getChild(int index) {
        throw new UnsupportedOperationException("员工没有下属");
    }
}

// 部门类 - 容器节点
public class Department implements OrganizationComponent {
    private String name;
    private String manager;
    private List<OrganizationComponent> children;
    
    public Department(String name, String manager) {
        this.name = name;
        this.manager = manager;
        this.children = new ArrayList<>();
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getPosition() {
        return "部门经理: " + manager;
    }
    
    @Override
    public double getSalary() {
        // 部门的薪资是下属薪资的总和
        return calculateTotalSalary();
    }
    
    @Override
    public void display() {
        System.out.println("部门: " + name + " - " + getPosition());
        for (OrganizationComponent child : children) {
            child.display();
        }
    }
    
    @Override
    public double calculateTotalSalary() {
        double total = 0;
        for (OrganizationComponent child : children) {
            total += child.calculateTotalSalary();
        }
        return total;
    }
    
    @Override
    public int countEmployees() {
        int count = 0;
        for (OrganizationComponent child : children) {
            count += child.countEmployees();
        }
        return count;
    }
    
    @Override
    public void printOrganizationChart() {
        System.out.println(name + " (" + manager + ")");
        for (OrganizationComponent child : children) {
            System.out.print("  ");
            child.printOrganizationChart();
        }
    }
    
    @Override
    public void add(OrganizationComponent component) {
        children.add(component);
    }
    
    @Override
    public void remove(OrganizationComponent component) {
        children.remove(component);
    }
    
    @Override
    public OrganizationComponent getChild(int index) {
        if (index >= 0 && index < children.size()) {
            return children.get(index);
        }
        return null;
    }
    
    // 部门特有的方法
    public int getEmployeeCount() {
        return countEmployees();
    }
    
    public List<OrganizationComponent> getSubDepartments() {
        return children.stream()
                .filter(child -> child instanceof Department)
                .collect(Collectors.toList());
    }
    
    public List<OrganizationComponent> getEmployees() {
        return children.stream()
                .filter(child -> child instanceof Employee)
                .collect(Collectors.toList());
    }
}

// 公司类 - 根容器
public class Company implements OrganizationComponent {
    private String name;
    private String ceo;
    private List<OrganizationComponent> departments;
    
    public Company(String name, String ceo) {
        this.name = name;
        this.ceo = ceo;
        this.departments = new ArrayList<>();
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getPosition() {
        return "CEO: " + ceo;
    }
    
    @Override
    public double getSalary() {
        return calculateTotalSalary();
    }
    
    @Override
    public void display() {
        System.out.println("公司: " + name);
        System.out.println("CEO: " + ceo);
        System.out.println("=== 组织架构 ===");
        for (OrganizationComponent dept : departments) {
            dept.display();
        }
    }
    
    @Override
    public double calculateTotalSalary() {
        double total = 0;
        for (OrganizationComponent dept : departments) {
            total += dept.calculateTotalSalary();
        }
        return total;
    }
    
    @Override
    public int countEmployees() {
        int count = 0;
        for (OrganizationComponent dept : departments) {
            count += dept.countEmployees();
        }
        return count;
    }
    
    @Override
    public void printOrganizationChart() {
        System.out.println("=== " + name + " 组织架构图 ===");
        System.out.println("CEO: " + ceo);
        for (OrganizationComponent dept : departments) {
            dept.printOrganizationChart();
        }
    }
    
    @Override
    public void add(OrganizationComponent component) {
        if (component instanceof Department) {
            departments.add(component);
        } else {
            throw new IllegalArgumentException("公司只能添加部门");
        }
    }
    
    @Override
    public void remove(OrganizationComponent component) {
        departments.remove(component);
    }
    
    @Override
    public OrganizationComponent getChild(int index) {
        if (index >= 0 && index < departments.size()) {
            return departments.get(index);
        }
        return null;
    }
    
    // 公司特有的方法
    public void generateSalaryReport() {
        System.out.println("=== " + name + " 薪资报告 ===");
        System.out.println("总员工数: " + countEmployees());
        System.out.println("总薪资支出: " + calculateTotalSalary());
        System.out.println("平均薪资: " + calculateTotalSalary() / countEmployees());
        
        for (OrganizationComponent dept : departments) {
            Department department = (Department) dept;
            System.out.println("\n部门: " + department.getName());
            System.out.println("  员工数: " + department.countEmployees());
            System.out.println("  部门薪资: " + department.calculateTotalSalary());
        }
    }
    
    public Department findDepartment(String name) {
        for (OrganizationComponent dept : departments) {
            Department department = (Department) dept;
            if (department.getName().equals(name)) {
                return department;
            }
        }
        return null;
    }
}

// 使用示例
public class OrganizationDemo {
    public static void main(String[] args) {
        // 创建公司
        Company company = new Company("科技有限公司", "张总裁");
        
        // 创建技术部门
        Department techDept = new Department("技术部", "李经理");
        techDept.add(new Employee("王工程师", "高级工程师", 15000));
        techDept.add(new Employee("赵工程师", "工程师", 12000));
        techDept.add(new Employee("钱工程师", "初级工程师", 8000));
        
        // 创建销售部门
        Department salesDept = new Department("销售部", "陈经理");
        salesDept.add(new Employee("周销售", "销售总监", 13000));
        salesDept.add(new Employee("吴销售", "销售代表", 9000));
        
        // 创建市场部门及其子部门
        Department marketDept = new Department("市场部", "孙经理");
        Department digitalMarketing = new Department("数字营销组", "郑组长");
        digitalMarketing.add(new Employee("冯专员", "SEO专员", 8500));
        digitalMarketing.add(new Employee("魏专员", "内容专员", 8200));
        
        marketDept.add(digitalMarketing);
        marketDept.add(new Employee("蒋专员", "品牌专员", 8800));
        
        // 组装公司结构
        company.add(techDept);
        company.add(salesDept);
        company.add(marketDept);
        
        // 统一处理
        System.out.println("=== 统一显示 ===");
        company.display();
        
        System.out.println("\n=== 组织架构图 ===");
        company.printOrganizationChart();
        
        System.out.println("\n=== 薪资报告 ===");
        company.generateSalaryReport();
        
        // 客户端无需区分具体类型
        System.out.println("\n=== 统计信息 ===");
        System.out.println("公司总员工数: " + company.countEmployees());
        System.out.println("公司总薪资: " + company.calculateTotalSalary());
        
        // 递归查找
        Department foundDept = company.findDepartment("技术部");
        if (foundDept != null) {
            System.out.println("\n找到部门: " + foundDept.getName());
            System.out.println("部门员工数: " + foundDept.countEmployees());
        }
    }
}

5. Spring框架中的组合模式

Spring框架中大量使用了组合模式的思想:

5.1 Spring Security的配置组合

java 复制代码
// 模拟Spring Security的配置组合
public interface SecurityConfig {
    void configure();
    String getName();
    void addConfig(SecurityConfig config);
    List<SecurityConfig> getChildren();
}

// 具体的安全配置
@Component
public class HttpSecurityConfig implements SecurityConfig {
    private List<SecurityConfig> configs = new ArrayList<>();
    private String name;
    
    public HttpSecurityConfig(String name) {
        this.name = name;
    }
    
    @Override
    public void configure() {
        System.out.println("配置HTTP安全: " + name);
        for (SecurityConfig config : configs) {
            config.configure();
        }
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public void addConfig(SecurityConfig config) {
        configs.add(config);
    }
    
    @Override
    public List<SecurityConfig> getChildren() {
        return new ArrayList<>(configs);
    }
    
    // HTTP安全特有的方法
    public HttpSecurityConfig authorizeRequests() {
        addConfig(new AuthorizeRequestsConfig());
        return this;
    }
    
    public HttpSecurityConfig formLogin() {
        addConfig(new FormLoginConfig());
        return this;
    }
    
    public HttpSecurityConfig logout() {
        addConfig(new LogoutConfig());
        return this;
    }
}

// 具体的配置项
@Component
public class AuthorizeRequestsConfig implements SecurityConfig {
    @Override
    public void configure() {
        System.out.println("  配置授权规则");
    }
    
    @Override
    public String getName() {
        return "authorizeRequests";
    }
    
    @Override
    public void addConfig(SecurityConfig config) {
        throw new UnsupportedOperationException("叶子配置不能添加子配置");
    }
    
    @Override
    public List<SecurityConfig> getChildren() {
        return Collections.emptyList();
    }
}

@Component 
public class FormLoginConfig implements SecurityConfig {
    @Override
    public void configure() {
        System.out.println("  配置表单登录");
    }
    
    @Override
    public String getName() {
        return "formLogin";
    }
    
    @Override
    public void addConfig(SecurityConfig config) {
        throw new UnsupportedOperationException("叶子配置不能添加子配置");
    }
    
    @Override
    public List<SecurityConfig> getChildren() {
        return Collections.emptyList();
    }
}

// 安全配置构建器
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
    
    @Bean
    public SecurityConfig securityConfig() {
        HttpSecurityConfig httpSecurity = new HttpSecurityConfig("httpSecurity");
        
        httpSecurity.authorizeRequests()
                   .formLogin()
                   .logout();
        
        return httpSecurity;
    }
    
    @Bean
    public SecurityConfigManager securityConfigManager() {
        return new SecurityConfigManager();
    }
}

// 安全配置管理器
@Component
public class SecurityConfigManager {
    private List<SecurityConfig> configs = new ArrayList<>();
    
    @Autowired
    public SecurityConfigManager(SecurityConfig... configs) {
        this.configs.addAll(Arrays.asList(configs));
    }
    
    public void configureAll() {
        System.out.println("=== 开始配置安全 ===");
        for (SecurityConfig config : configs) {
            config.configure();
        }
        System.out.println("=== 安全配置完成 ===");
    }
    
    public void addConfig(SecurityConfig config) {
        configs.add(config);
    }
    
    public List<SecurityConfig> getAllConfigs() {
        List<SecurityConfig> allConfigs = new ArrayList<>();
        collectConfigs(configs, allConfigs);
        return allConfigs;
    }
    
    private void collectConfigs(List<SecurityConfig> current, List<SecurityConfig> result) {
        for (SecurityConfig config : current) {
            result.add(config);
            collectConfigs(config.getChildren(), result);
        }
    }
}

5.2 Spring Bean定义的组合

java 复制代码
// 模拟Spring Bean定义组合
public interface BeanDefinition {
    String getBeanName();
    Class<?> getBeanClass();
    void validate();
    List<BeanDefinition> getDependsOn();
}

// 具体的Bean定义
public class RootBeanDefinition implements BeanDefinition {
    private String beanName;
    private Class<?> beanClass;
    private List<BeanDefinition> dependencies = new ArrayList<>();
    
    public RootBeanDefinition(String beanName, Class<?> beanClass) {
        this.beanName = beanName;
        this.beanClass = beanClass;
    }
    
    @Override
    public String getBeanName() {
        return beanName;
    }
    
    @Override
    public Class<?> getBeanClass() {
        return beanClass;
    }
    
    @Override
    public void validate() {
        System.out.println("验证Bean定义: " + beanName);
        for (BeanDefinition dependency : dependencies) {
            dependency.validate();
        }
    }
    
    @Override
    public List<BeanDefinition> getDependsOn() {
        return new ArrayList<>(dependencies);
    }
    
    public void addDependency(BeanDefinition dependency) {
        dependencies.add(dependency);
    }
}

// Bean定义注册表
@Component
public class BeanDefinitionRegistry {
    private Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
    
    public void registerBeanDefinition(String name, BeanDefinition definition) {
        beanDefinitions.put(name, definition);
    }
    
    public BeanDefinition getBeanDefinition(String name) {
        return beanDefinitions.get(name);
    }
    
    public void validateAll() {
        System.out.println("=== 开始验证所有Bean定义 ===");
        for (BeanDefinition definition : beanDefinitions.values()) {
            definition.validate();
        }
    }
    
    public List<String> getBeanDependencyTree(String beanName) {
        List<String> tree = new ArrayList<>();
        buildDependencyTree(beanName, tree, 0);
        return tree;
    }
    
    private void buildDependencyTree(String beanName, List<String> tree, int level) {
        BeanDefinition definition = beanDefinitions.get(beanName);
        if (definition == null) return;
        
        String indent = "  ".repeat(level);
        tree.add(indent + beanName);
        
        for (BeanDefinition dependency : definition.getDependsOn()) {
            buildDependencyTree(dependency.getBeanName(), tree, level + 1);
        }
    }
}

6. 组合模式的进阶用法

6.1 访问者模式与组合模式结合

java 复制代码
// 访问者接口
public interface ComponentVisitor {
    void visit(File file);
    void visit(Directory directory);
    void visit(Company company);
    void visit(Department department);
    void visit(Employee employee);
}

// 具体的访问者
public class SalaryReportVisitor implements ComponentVisitor {
    private double totalSalary = 0;
    private int totalEmployees = 0;
    
    @Override
    public void visit(File file) {
        // 文件不需要处理
    }
    
    @Override
    public void visit(Directory directory) {
        // 目录本身没有薪资
    }
    
    @Override
    public void visit(Company company) {
        System.out.println("=== 公司薪资报告 ===");
        System.out.println("公司: " + company.getName());
    }
    
    @Override
    public void visit(Department department) {
        System.out.println("部门: " + department.getName() + 
                          " - 员工数: " + department.countEmployees() +
                          " - 总薪资: " + department.calculateTotalSalary());
    }
    
    @Override
    public void visit(Employee employee) {
        totalSalary += employee.getSalary();
        totalEmployees++;
        System.out.println("  员工: " + employee.getName() + 
                          " - 职位: " + employee.getPosition() +
                          " - 薪资: " + employee.getSalary());
    }
    
    public void printSummary() {
        System.out.println("=== 报告总结 ===");
        System.out.println("总员工数: " + totalEmployees);
        System.out.println("总薪资: " + totalSalary);
        System.out.println("平均薪资: " + (totalEmployees > 0 ? totalSalary / totalEmployees : 0));
    }
}

// 在组件接口中添加接受访问者的方法
public interface OrganizationComponent {
    // ... 原有方法
    
    void accept(ComponentVisitor visitor);
}

// 在具体组件中实现accept方法
public class Employee implements OrganizationComponent {
    // ... 原有实现
    
    @Override
    public void accept(ComponentVisitor visitor) {
        visitor.visit(this);
    }
}

public class Department implements OrganizationComponent {
    // ... 原有实现
    
    @Override
    public void accept(ComponentVisitor visitor) {
        visitor.visit(this);
        for (OrganizationComponent child : children) {
            child.accept(visitor);
        }
    }
}

public class Company implements OrganizationComponent {
    // ... 原有实现
    
    @Override
    public void accept(ComponentVisitor visitor) {
        visitor.visit(this);
        for (OrganizationComponent dept : departments) {
            dept.accept(visitor);
        }
    }
}

// 使用访问者
public class VisitorDemo {
    public static void main(String[] args) {
        Company company = createCompany();
        
        SalaryReportVisitor visitor = new SalaryReportVisitor();
        company.accept(visitor);
        visitor.printSummary();
    }
    
    private static Company createCompany() {
        // 创建公司结构的代码...
        return new Company(" demo公司", "demoCEO");
    }
}

6.2 组合模式与建造者模式结合

java 复制代码
// 组合建造者
public class OrganizationBuilder {
    private OrganizationComponent root;
    
    public OrganizationBuilder company(String name, String ceo) {
        root = new Company(name, ceo);
        return this;
    }
    
    public OrganizationBuilder department(String name, String manager) {
        if (root == null) {
            throw new IllegalStateException("请先创建公司");
        }
        
        Department dept = new Department(name, manager);
        root.add(dept);
        return this;
    }
    
    public OrganizationBuilder employee(String name, String position, double salary) {
        if (root == null) {
            throw new IllegalStateException("请先创建公司");
        }
        
        // 找到最后一个部门添加员工
        OrganizationComponent lastDept = findLastDepartment(root);
        if (lastDept != null) {
            lastDept.add(new Employee(name, position, salary));
        } else {
            throw new IllegalStateException("请先创建部门");
        }
        return this;
    }
    
    private OrganizationComponent findLastDepartment(OrganizationComponent component) {
        if (component instanceof Company) {
            Company company = (Company) component;
            List<OrganizationComponent> depts = company.getChildren();
            return depts.isEmpty() ? null : depts.get(depts.size() - 1);
        } else if (component instanceof Department) {
            Department dept = (Department) component;
            List<OrganizationComponent> children = dept.getChildren();
            // 查找最后一个部门
            for (int i = children.size() - 1; i >= 0; i--) {
                OrganizationComponent child = children.get(i);
                if (child instanceof Department) {
                    return child;
                }
            }
            return dept;
        }
        return null;
    }
    
    public OrganizationComponent build() {
        return root;
    }
}

// 流畅的API使用
public class BuilderDemo {
    public static void main(String[] args) {
        OrganizationComponent company = new OrganizationBuilder()
            .company("科技有限公司", "张总裁")
            .department("技术部", "李经理")
                .employee("王工程师", "高级工程师", 15000)
                .employee("赵工程师", "工程师", 12000)
            .department("销售部", "陈经理")
                .employee("周销售", "销售总监", 13000)
                .employee("吴销售", "销售代表", 9000)
            .build();
        
        company.display();
    }
}

7. 组合模式 vs 其他模式

7.1 组合模式 vs 装饰器模式

  • 组合模式:处理部分-整体关系,统一处理叶子节点和容器节点
  • 装饰器模式:动态添加功能,不改变接口但增强行为

7.2 组合模式 vs 享元模式

  • 组合模式:处理树形结构,关注对象间的层次关系
  • 享元模式:共享对象以减少内存使用,关注对象的内在状态共享

7.3 组合模式 vs 迭代器模式

  • 组合模式:定义树形结构和统一接口
  • 迭代器模式:提供遍历集合的方法,常与组合模式结合使用

8. 总结与思考

8.1 组合模式的优点

  1. 统一处理:客户端可以一致地处理单个对象和对象组合
  2. 简化客户端:客户端不需要关心处理的是单个对象还是整个结构
  3. 易于扩展:新增组件类型很容易,符合开闭原则
  4. 灵活的结构:可以构建复杂的树形结构

8.2 组合模式的缺点

  1. 设计较复杂:需要正确识别出叶子和容器节点
  2. 类型系统限制:在类型检查严格的语言中实现可能较复杂
  3. 性能考虑:深度递归可能影响性能
  4. 叶子节点限制:叶子节点需要实现一些无意义的方法

8.3 设计思考

组合模式的本质是**"递归组合"**。它通过统一的接口让客户端可以无差别地处理单个对象和对象组合,从而简化了复杂树形结构的处理。

深入思考的角度

"组合模式的核心价值在于它提供了一种处理树形结构的优雅方式。它通过递归组合和统一接口,让复杂的层次结构变得简单可控。组合模式是'部分-整体'思维在面向对象设计中的完美体现。"

在实际应用中,组合模式有很多优秀的实践:

  • Java AWT/Swing中的组件体系
  • XML DOM解析器的节点结构
  • 文件系统的目录结构
  • 组织架构的管理系统

从系统设计的角度看,组合模式特别适合以下场景:

  • 需要表示对象的部分-整体层次结构
  • 希望客户端忽略组合对象与单个对象的差异
  • 需要构建树形结构并表示复杂的层次关系

最佳实践建议

  1. 仔细设计组件接口,确保它适用于所有类型的组件
  2. 考虑使用透明式还是安全式组合模式
  3. 为组合结构提供合适的迭代器
  4. 考虑与访问者模式结合以添加新操作
  5. 注意递归深度,避免栈溢出

使用场景判断

  • 适合:树形结构、部分-整体关系、统一处理需求
  • 不适合:组件间差异很大、结构简单、性能要求极高

下一篇预告:设计模式手册011 - 享元模式:如何通过共享技术有效地支持大量细粒度对象?


版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

相关推荐
帅中的小灰灰1 小时前
C++编程策略设计模式
开发语言·c++·设计模式
q***47181 小时前
Spring Boot 3.3.4 升级导致 Logback 之前回滚策略配置不兼容问题解决
java·spring boot·logback
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于SpringBoot的医院血库管理系统设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
菠菠萝宝2 小时前
【Java手搓RAGFlow】-9- RAG对话实现
java·开发语言·人工智能·llm·jenkins·openai
清风徐来QCQ2 小时前
Spring Boot 静态资源路径映射
java·spring boot·后端
科威舟的代码笔记3 小时前
第10讲:Stream实战与陷阱——综合案例与最佳实践
java·开发语言
程序定小飞3 小时前
基于springboot的体育馆使用预约平台的设计与实现
java·开发语言·spring boot·后端·spring
5***79003 小时前
Java虚拟现实开发
java·开发语言·vr
计算机毕业设计小途3 小时前
计算机毕业设计推荐:基于SpringBoot的水产养殖管理系统【Java+spring boot+MySQL、Java项目、Java毕设、Java项目定制定做】
java·spring boot·mysql