设计模式手册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 组合模式的优点
- 统一处理:客户端可以一致地处理单个对象和对象组合
- 简化客户端:客户端不需要关心处理的是单个对象还是整个结构
- 易于扩展:新增组件类型很容易,符合开闭原则
- 灵活的结构:可以构建复杂的树形结构
8.2 组合模式的缺点
- 设计较复杂:需要正确识别出叶子和容器节点
- 类型系统限制:在类型检查严格的语言中实现可能较复杂
- 性能考虑:深度递归可能影响性能
- 叶子节点限制:叶子节点需要实现一些无意义的方法
8.3 设计思考
组合模式的本质是**"递归组合"**。它通过统一的接口让客户端可以无差别地处理单个对象和对象组合,从而简化了复杂树形结构的处理。
深入思考的角度:
"组合模式的核心价值在于它提供了一种处理树形结构的优雅方式。它通过递归组合和统一接口,让复杂的层次结构变得简单可控。组合模式是'部分-整体'思维在面向对象设计中的完美体现。"
在实际应用中,组合模式有很多优秀的实践:
- Java AWT/Swing中的组件体系
- XML DOM解析器的节点结构
- 文件系统的目录结构
- 组织架构的管理系统
从系统设计的角度看,组合模式特别适合以下场景:
- 需要表示对象的部分-整体层次结构
- 希望客户端忽略组合对象与单个对象的差异
- 需要构建树形结构并表示复杂的层次关系
最佳实践建议:
- 仔细设计组件接口,确保它适用于所有类型的组件
- 考虑使用透明式还是安全式组合模式
- 为组合结构提供合适的迭代器
- 考虑与访问者模式结合以添加新操作
- 注意递归深度,避免栈溢出
使用场景判断:
- 适合:树形结构、部分-整体关系、统一处理需求
- 不适合:组件间差异很大、结构简单、性能要求极高
下一篇预告:设计模式手册011 - 享元模式:如何通过共享技术有效地支持大量细粒度对象?
版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。