当前位置: 首页 > news >正文

【设计模式08】组合模式

【设计模式08】组合模式

一、从业务痛点谈起

还记得我们第一次接手企业组织架构管理系统时的困境吗?一个看似简单的需求:“统计某个部门及其所有下级部门的总人数”,却让团队陷入了代码泥潭。

最初的实现是这样的:先查询一级部门,再循环查询二级部门,接着是三级、四级…代码中充斥着嵌套的if-else和for循环。更糟糕的是,当组织架构层级发生变化时,代码就得重写。某大型集团的组织架构甚至达到了8层,维护这样的代码简直是噩梦。

这时候,我们需要一种设计模式,能够统一处理单个对象和组合对象,让客户端无需关心处理的是叶子节点还是组合节点——这就是组合模式的价值所在。

二、组合模式的核心思想

2.1 定义与本质

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

打个生活化的比方:组合模式就像俄罗斯套娃,每个套娃既可以是独立的个体,也可以包含其他套娃。无论是操作单个套娃还是一整套,方式都是一样的。

2.2 核心结构解析

contains
«abstract»
Component
+String name
+add(Component c)
+remove(Component c)
+display(int depth)
+calculateCount()
Leaf
+add(Component c)
+remove(Component c)
+display(int depth)
+calculateCount()
Composite
-List<Component> children
+add(Component c)
+remove(Component c)
+display(int depth)
+calculateCount()

组合模式的三个核心角色:

  • Component(抽象组件):定义叶子和容器的共同接口
  • Leaf(叶子组件):树形结构的叶子节点,没有子节点
  • Composite(容器组件):可以包含子节点的容器对象

2.3 适用场景分析

场景特征具体描述典型案例
树形结构数据本身具有层次关系组织架构、文件系统、菜单管理
统一处理希望统一处理个体和组合图形编辑器、UI组件树
递归操作需要递归遍历整个结构权限管理、规则引擎
动态扩展结构层级可能动态变化商品分类、地区管理

三、实现思路与关键代码

3.1 核心逻辑实现

让我们通过企业组织架构的例子,展示组合模式的几种实现方式:

方式一:透明式组合模式(推荐)

// 抽象组件:定义所有组件的公共接口
public abstract class OrganizationComponent {protected String name;protected String description;public OrganizationComponent(String name, String description) {this.name = name;this.description = description;}// 默认实现,叶子节点会抛出异常public void add(OrganizationComponent component) {throw new UnsupportedOperationException("不支持添加操作");}public void remove(OrganizationComponent component) {throw new UnsupportedOperationException("不支持删除操作");}// 抽象方法,强制子类实现public abstract void display(int depth);public abstract int calculateEmployeeCount();
}// 叶子节点:员工
public class Employee extends OrganizationComponent {private String position;public Employee(String name, String position) {super(name, position);this.position = position;}@Overridepublic void display(int depth) {// 打印缩进,体现层级关系System.out.println("-".repeat(depth) + " 员工:" + name + ",职位:" + position);}@Overridepublic int calculateEmployeeCount() {return 1; // 员工算1个人}
}// 容器节点:部门
public class Department extends OrganizationComponent {// 存储子组件private List<OrganizationComponent> components = new ArrayList<>();public Department(String name, String description) {super(name, description);}@Overridepublic void add(OrganizationComponent component) {components.add(component);}@Overridepublic void remove(OrganizationComponent component) {components.remove(component);}@Overridepublic void display(int depth) {System.out.println("-".repeat(depth) + " 部门:" + name);// 递归显示所有子组件for (OrganizationComponent component : components) {component.display(depth + 2);}}@Overridepublic int calculateEmployeeCount() {int count = 0;// 递归计算所有子组件的人数for (OrganizationComponent component : components) {count += component.calculateEmployeeCount();}return count;}
}

方式二:安全式组合模式

// 抽象组件:只定义公共方法
public abstract class OrganizationUnit {protected String name;public OrganizationUnit(String name) {this.name = name;}// 只定义所有组件都有的方法public abstract void display(int depth);public abstract int getStaffCount();
}// 容器接口:定义容器特有的方法
public interface IContainer {void add(OrganizationUnit unit);void remove(OrganizationUnit unit);List<OrganizationUnit> getChildren();
}// 部门实现
public class DepartmentSafe extends OrganizationUnit implements IContainer {private List<OrganizationUnit> children = new ArrayList<>();@Overridepublic void add(OrganizationUnit unit) {children.add(unit);}@Overridepublic void remove(OrganizationUnit unit) {children.remove(unit);}@Overridepublic List<OrganizationUnit> getChildren() {return children;}@Overridepublic void display(int depth) {System.out.println("-".repeat(depth) + " 部门:" + name);for (OrganizationUnit child : children) {child.display(depth + 2);}}@Overridepublic int getStaffCount() {return children.stream().mapToInt(OrganizationUnit::getStaffCount).sum();}
}// 员工实现
public class EmployeeSafe extends OrganizationUnit {@Overridepublic void display(int depth) {System.out.println("-".repeat(depth) + " 员工:" + name);}@Overridepublic int getStaffCount() {return 1;}
}

方式三:带缓存优化的组合模式

public class CachedDepartment extends OrganizationComponent {private List<OrganizationComponent> components = new ArrayList<>();private Integer cachedEmployeeCount; // 缓存员工数量private boolean isDirty = true; // 标记缓存是否失效@Overridepublic void add(OrganizationComponent component) {components.add(component);isDirty = true; // 添加后缓存失效}@Overridepublic void remove(OrganizationComponent component) {components.remove(component);isDirty = true; // 删除后缓存失效}@Overridepublic int calculateEmployeeCount() {if (isDirty || cachedEmployeeCount == null) {// 重新计算并缓存cachedEmployeeCount = components.stream().mapToInt(OrganizationComponent::calculateEmployeeCount).sum();isDirty = false;}return cachedEmployeeCount;}
}

3.2 不同实现方式对比

实现方式优点缺点适用场景
透明式客户端调用简单统一叶子节点包含无意义方法结构相对稳定,强调使用便利性
安全式接口定义清晰,类型安全客户端需要区分类型注重类型安全,结构复杂多变
带缓存性能优化,避免重复计算实现复杂,需要维护缓存一致性大规模数据,频繁查询场景

四、企业级应用案例

4.1 案例一:美团组织架构权限管理系统

背景与问题:
美团的组织架构极其复杂,包含总部、城市分公司、业务线、部门等多个层级。权限管理需求:

  • 上级部门自动拥有下级部门的所有权限
  • 支持按部门批量授权
  • 能够快速查询某员工的所有权限(包括继承的)

模式应用方案:

public class PermissionComponent {// 权限树节点public abstract class PermissionNode {protected String nodeId;protected Set<String> directPermissions = new HashSet<>();// 获取所有权限(包括继承的)public abstract Set<String> getAllPermissions();// 检查是否有某权限public boolean hasPermission(String permission) {return getAllPermissions().contains(permission);}}// 部门节点public class DepartmentNode extends PermissionNode {private List<PermissionNode> children = new ArrayList<>();private Department department;@Overridepublic Set<String> getAllPermissions() {Set<String> allPermissions = new HashSet<>(directPermissions);// 递归收集所有子部门的权限for (PermissionNode child : children) {allPermissions.addAll(child.getAllPermissions());}return allPermissions;}// 批量授权public void grantPermissionToAll(String permission) {this.directPermissions.add(permission);// 自动传播到所有子节点for (PermissionNode child : children) {if (child instanceof DepartmentNode) {((DepartmentNode) child).grantPermissionToAll(permission);}}}}// 员工节点public class EmployeeNode extends PermissionNode {private Employee employee;private DepartmentNode department; // 所属部门@Overridepublic Set<String> getAllPermissions() {Set<String> allPermissions = new HashSet<>(directPermissions);// 继承部门权限if (department != null) {allPermissions.addAll(department.getAllPermissions());}return allPermissions;}}
}

实施效果:

  • 权限继承关系清晰,维护成本降低60%
  • 批量授权效率提升10倍
  • 支持灵活的组织架构调整,代码零修改

4.2 案例二:京东商品分类体系

背景与问题:
京东的商品分类体系包含数万个类目,层级深度不一(3-7层)。需求包括:

  • 统计某分类下的所有商品数量
  • 批量调整分类下所有商品的属性
  • 生成分类面包屑导航

模式应用方案:

public abstract class CategoryComponent {protected Long categoryId;protected String categoryName;protected Integer level;// 生成面包屑public abstract String generateBreadcrumb();// 统计商品数量public abstract int countProducts();// 批量操作public abstract void batchUpdate(Map<String, Object> attributes);
}public class Category extends CategoryComponent {private List<CategoryComponent> subCategories = new ArrayList<>();private CategoryComponent parent;@Overridepublic String generateBreadcrumb() {if (parent == null) {return categoryName;}return parent.generateBreadcrumb() + " > " + categoryName;}@Overridepublic int countProducts() {// 使用并行流提升性能return subCategories.parallelStream().mapToInt(CategoryComponent::countProducts).sum();}@Overridepublic void batchUpdate(Map<String, Object> attributes) {// 异步批量更新CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {updateSelf(attributes);// 递归更新子分类List<CompletableFuture<Void>> futures = subCategories.stream().map(sub -> CompletableFuture.runAsync(() -> sub.batchUpdate(attributes))).collect(Collectors.toList());CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();});}
}

五、模式深度剖析

5.1 优势与局限

优势:

  • 简化客户端代码:客户端无需关心处理的是单个对象还是组合对象
  • 扩展性强:易于增加新的节点类型
  • 符合开闭原则:对扩展开放,对修改关闭
  • 递归处理自然:天然支持递归操作

局限:

  • 设计复杂度:可能使设计变得过于一般化
  • 类型安全问题:透明式组合中,叶子节点包含无意义的方法
  • 性能考虑:深层递归可能导致栈溢出
  • 难以限制组件类型:难以限制容器中的组件类型

5.2 线程安全考量

public class ThreadSafeDepartment extends OrganizationComponent {// 使用线程安全的集合private final CopyOnWriteArrayList<OrganizationComponent> components = new CopyOnWriteArrayList<>();// 使用读写锁优化性能private final ReadWriteLock lock = new ReentrantReadWriteLock();@Overridepublic void add(OrganizationComponent component) {lock.writeLock().lock();try {components.add(component);} finally {lock.writeLock().unlock();}}@Overridepublic int calculateEmployeeCount() {lock.readLock().lock();try {return components.stream().mapToInt(OrganizationComponent::calculateEmployeeCount).sum();} finally {lock.readLock().unlock();}}
}

5.3 性能影响分析

操作类型时间复杂度优化策略
遍历O(n)使用并行流、分页加载
查找特定节点O(n)建立索引Map、使用缓存
添加/删除O(1)使用高效数据结构
统计计算O(n)缓存计算结果、增量更新

六、框架中的应用

6.1 Spring中的组合模式

Spring中多处使用了组合模式:

// Spring的CompositeMessageSource
public class CompositeMessageSource implements MessageSource {private final List<MessageSource> messageSources = new ArrayList<>();public void addMessageSource(MessageSource messageSource) {this.messageSources.add(messageSource);}@Overridepublic String getMessage(String code, Object[] args, Locale locale) {for (MessageSource messageSource : messageSources) {try {return messageSource.getMessage(code, args, locale);} catch (NoSuchMessageException ex) {// 继续尝试下一个MessageSource}}throw new NoSuchMessageException(code, locale);}
}// Spring Security的投票机制
public class AffirmativeBased extends AbstractAccessDecisionManager {public void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes) {// 遍历所有投票器(组合模式)for (AccessDecisionVoter voter : getDecisionVoters()) {int result = voter.vote(authentication, object, configAttributes);if (result == AccessDecisionVoter.ACCESS_GRANTED) {return; // 只要有一个同意就通过}}}
}

6.2 MyBatis中的应用

// MyBatis的SqlNode体系
public interface SqlNode {boolean apply(DynamicContext context);
}public class MixedSqlNode implements SqlNode {private final List<SqlNode> contents;@Overridepublic boolean apply(DynamicContext context) {// 组合多个SqlNodecontents.forEach(node -> node.apply(context));return true;}
}

七、与相似模式的辨析

7.1 组合模式 vs 装饰器模式

对比维度组合模式装饰器模式
设计目的表示"部分-整体"层次动态添加职责
结构关系树形结构链式结构
对象数量可包含多个子对象一般包装一个对象
使用重点统一处理个体和组合透明地扩展功能

7.2 组合模式 vs 迭代器模式

组合模式经常与迭代器模式配合使用:

public class DepartmentWithIterator extends Department implements Iterable<OrganizationComponent> {@Overridepublic Iterator<OrganizationComponent> iterator() {return new DepartmentIterator();}private class DepartmentIterator implements Iterator<OrganizationComponent> {private Stack<Iterator<OrganizationComponent>> stack = new Stack<>();public DepartmentIterator() {stack.push(components.iterator());}@Overridepublic boolean hasNext() {if (stack.empty()) return false;Iterator<OrganizationComponent> iterator = stack.peek();if (!iterator.hasNext()) {stack.pop();return hasNext();}return true;}@Overridepublic OrganizationComponent next() {if (hasNext()) {Iterator<OrganizationComponent> iterator = stack.peek();OrganizationComponent component = iterator.next();if (component instanceof Department) {stack.push(((Department) component).components.iterator());}return component;}return null;}}
}

八、实践启示

通过对组合模式的深入剖析和实战应用,我们得到以下核心启示:

  • 递归思维的力量:组合模式将递归思维优雅地融入面向对象设计中,让我们能够用统一的方式处理复杂的树形结构。这启发我们在面对层次性数据时,要善于发现和利用递归特性。

  • 接口统一的价值:通过统一叶子节点和容器节点的接口,组合模式极大地简化了客户端代码。这提醒我们,良好的抽象能够隐藏复杂性,提供简洁的使用体验。

  • 性能与设计的权衡:在实际应用中,我们需要在设计的优雅性和性能之间找到平衡点。缓存机制、并行处理、懒加载等优化手段都是必要的补充。

  • 安全性不应被忽视:选择透明式还是安全式组合,取决于我们对类型安全的重视程度。在关键业务系统中,类型安全往往比使用便利性更重要。

  • 模式组合的威力:组合模式与访问者模式、迭代器模式、责任链模式等配合使用,能够构建出功能强大且灵活的系统架构。

记住,组合模式不仅仅是一种设计模式,更是一种思维方式——当我们面对"整体与部分"的关系时,要思考如何让它们和谐共存,统一处理

http://www.dtcms.com/a/347399.html

相关文章:

  • LLaMA-Factory 中配置文件或命令行里各个参数的含义
  • 深度集成Dify API:基于Vue 3的智能对话前端解决方案
  • Maven仓库与Maven私服架构
  • Vue 3 useModel vs defineModel:选择正确的双向绑定方案
  • 自然语言处理——05 Transformer架构和手写实现
  • 微前端架构核心要点对比
  • 小区物业对大楼顶面的巡查通常是定期巡查+特殊情况下的临时巡查相结合
  • 认识模块化及常见考点
  • 【秋招笔试】2025.08.23京东秋招笔试题
  • onnx入门教程(二)—— PyTorch 转 ONNX 详解
  • C#多线程同步利器:Monitor全解析
  • 安卓10.0系统修改定制化____如何修改固件 去除开机后默认的屏幕锁定
  • AcWing 114. 【0x07】国王游戏
  • C代码学习笔记(一)
  • Windows打开命令窗口的几种方式
  • 使用 PSRP 通过 SSH 建立 WinRM 隧道
  • 注意力机制中为什么q与k^T相乘是注意力分数
  • 每日定投40刀BTC(22)20250802 - 20250823
  • 编程刷题-染色题DFS
  • 03_数据结构
  • 在 CentOS 7 上搭建 OpenTenBase 集群:从源码到生产环境的全流程指南
  • MSPM0G3507工程模板创建
  • 微信小程序自定义组件开发(上):从创建到数据通信详解(五)
  • 纠删码技术,更省钱的分布式系统的可靠性技术
  • 使用springboot开发-AI智能体平台管理系统,统一管理各个平台的智能体并让智能体和AI语音设备通信,做一个属于自己的小艾同学~
  • Dubbo vs Feign
  • 个人思考与发展
  • 探秘北斗卫星导航系统(BDS):架构、应用与未来蓝图,展现中国力量
  • 详细说一说JIT
  • Redis面试精讲 Day 28:Redis云原生部署与Kubernetes集成