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

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

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

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

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

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

传统做法的困境

// 不同的类需要不同的处理方式
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 核心角色

// 组件接口:定义叶子和容器的共同接口
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 安全式

透明式组合模式
// 透明式:在组件接口中声明所有方法,包括管理子组件的方法
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();
}
}
安全式组合模式
// 安全式:在组件接口中只声明公共方法,管理子组件的方法在容器接口中声明
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 第二重:递归组合的力量

// 利用递归处理复杂树形结构
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 第三重:组合迭代器

// 为组合结构提供迭代器
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. 实战案例:完整的组织架构系统

// 组织组件接口
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的配置组合

// 模拟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定义的组合

// 模拟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 访问者模式与组合模式结合

// 访问者接口
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 组合模式与建造者模式结合

// 组合建造者
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版权协议,转载请附上原文出处链接及本声明。

posted on 2026-01-28 12:22  ljbguanli  阅读(0)  评论(0)    收藏  举报