设计模式详解-组合模式
设计模式详解:组合模式
一、模式概述
组合模式(Composite Pattern)是结构型设计模式中最具递归美感的模式,其核心意图在于将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。这一模式让客户端能够用统一的接口处理原子对象和复合对象,无需区分二者,从而简化了树形结构的操作逻辑。
组合模式的命名直接揭示了其本质——"组合"即部分构成整体,整体又成为更大整体的组成部分。自然界中,细胞组成组织,组织构成器官,器官形成系统;文件系统中,文件组成目录,目录嵌套目录,形成层级结构;组织架构中,员工向经理汇报,经理向总监汇报,总监向副总裁汇报。这些场景的共同特征是递归的层次结构,而组合模式正是对此类结构的软件抽象。
组合模式的深层价值在于透明性与一致性的统一。没有组合模式时,客户端必须编写条件分支来区分处理叶子节点和分支节点,代码充斥着if (node.isLeaf())的判断。组合模式通过让二者实现同一接口,消除了这种区分,使递归算法得以优雅表达。
二、模式结构
组合模式包含三个核心角色,形成递归的树形结构:
组件接口(Component):定义叶子节点和复合节点的共同接口,声明访问和管理子组件的方法。可为接口或抽象类,根据需要提供默认实现。
叶子节点(Leaf):表示树中的原子对象,没有子节点。实现组件接口的基本行为。
复合节点(Composite):表示有子节点的分支对象。实现组件接口,并存储子组件集合,定义增删查等管理操作。在调用自身行为时,通常递归委托给子组件。
组合模式的关键设计决策在于组件接口的丰富程度。安全版本仅在Composite中定义子组件管理方法,客户端需类型转换才能调用;透明版本在Component中统一声明所有方法,Leaf对管理方法提供空实现或抛异常。透明版本牺牲类型安全换取接口一致性,是更常见的实践。
三、深度案例:企业级权限管理系统
以下展示一个真实场景下的组合模式应用——支持多维度、多层级、动态继承的企业级权限管理系统,涵盖组织架构、资源目录、角色继承、审批流程等复杂树形结构。
3.1 组件接口:权限节点抽象
java
/**
* 组件接口:权限模型中的可授权实体
* 采用透明组合设计:所有操作统一声明,叶子节点提供空实现
*/
public interface Securable {
// ========== 身份标识 ==========
String getId();
String getName();
SecurableType getType();
// ========== 权限判定(核心行为) ==========
/**
* 判定主体对此对象是否拥有指定权限
*/
boolean checkPermission(Subject subject, Permission permission);
/**
* 获取主体在此对象上的有效权限集合
* 考虑直接授权、继承、角色叠加等所有因素
*/
Set<Permission> resolveEffectivePermissions(Subject subject);
// ========== 树形结构操作(透明组合) ==========
/**
* 添加子节点(叶子节点空实现)
*/
default void addChild(Securable child) {
throw new UnsupportedOperationException("叶子节点不支持添加子节点");
}
/**
* 移除子节点(叶子节点空实现)
*/
default void removeChild(Securable child) {
throw new UnsupportedOperationException("叶子节点不支持移除子节点");
}
/**
* 获取子节点列表(叶子节点返回空列表)
*/
default List<Securable> getChildren() {
return Collections.emptyList();
}
/**
* 获取父节点
*/
Securable getParent();
/**
* 设置父节点
*/
void setParent(Securable parent);
// ========== 路径与层级操作 ==========
/**
* 获取从根节点到当前节点的完整路径
*/
default List<Securable> getPath() {
List<Securable> path = new ArrayList<>();
Securable current = this;
while (current != null) {
path.add(0, current); // 头插法保持根到叶的顺序
current = current.getParent();
}
return path;
}
/**
* 获取层级深度(根节点为0)
*/
default int getDepth() {
return getPath().size() - 1;
}
/**
* 获取根节点
*/
default Securable getRoot() {
List<Securable> path = getPath();
return path.isEmpty() ? null : path.get(0);
}
// ========== 遍历操作 ==========
/**
* 前序遍历:先访问自身,再递归访问子节点
*/
default void preOrder(Consumer<Securable> visitor) {
visitor.accept(this);
for (Securable child : getChildren()) {
child.preOrder(visitor);
}
}
/**
* 后序遍历:先递归访问子节点,再访问自身
*/
default void postOrder(Consumer<Securable> visitor) {
for (Securable child : getChildren()) {
child.postOrder(visitor);
}
visitor.accept(this);
}
/**
* 按条件查找所有匹配的子孙节点
*/
default List<Securable> findAll(Predicate<Securable> predicate) {
List<Securable> result = new ArrayList<>();
preOrder(node -> {
if (predicate.test(node)) {
result.add(node);
}
});
return result;
}
}
3.2 叶子节点:原子权限实体
java
/**
* 叶子节点:具体资源(文件、菜单、API端点、数据字段等)
*/
public class Resource implements Securable {
private String id;
private String name;
private ResourceType resourceType; // FILE, MENU, API, DATA_FIELD等
private String resourceLocator; // 资源定位符:路径、URL、表名.字段名等
private Securable parent;
// 直接授权规则:谁对此资源有什么权限
private Map<String, Set<Permission>> directGrants = new HashMap<>();
// 资源特定的权限集合(不同资源类型有不同权限)
private Set<Permission> supportedPermissions;
// 数据范围规则(用于数据权限)
private DataScopeRule dataScopeRule;
public Resource(String id, String name, ResourceType type, String locator) {
this.id = id;
this.name = name;
this.resourceType = type;
this.resourceLocator = locator;
}
@Override
public String getId() { return id; }
@Override
public String getName() { return name; }
@Override
public SecurableType getType() { return SecurableType.RESOURCE; }
@Override
public Securable getParent() { return parent; }
@Override
public void setParent(Securable parent) { this.parent = parent; }
@Override
public boolean checkPermission(Subject subject, Permission permission) {
// 1. 检查直接授权
Set<Permission> direct = directGrants.get(subject.getId());
if (direct != null && direct.contains(permission)) {
return true;
}
// 2. 检查角色授权(通过Subject的角色间接获得)
for (Role role : subject.getRoles()) {
if (role.checkPermission(this, permission)) {
return true;
}
}
// 3. 检查继承授权(从父资源继承)
if (parent != null && inheritsFromParent()) {
return parent.checkPermission(subject, permission);
}
return false;
}
@Override
public Set<Permission> resolveEffectivePermissions(Subject subject) {
Set<Permission> effective = new HashSet<>();
// 直接授权
Set<Permission> direct = directGrants.get(subject.getId());
if (direct != null) effective.addAll(direct);
// 角色授权
for (Role role : subject.getRoles()) {
effective.addAll(role.getPermissions(this));
}
// 继承授权
if (parent != null && inheritsFromParent()) {
effective.addAll(parent.resolveEffectivePermissions(subject));
}
// 过滤为资源支持的权限
effective.retainAll(supportedPermissions);
return effective;
}
// 资源特定方法
public void grant(String subjectId, Permission... permissions) {
directGrants.computeIfAbsent(subjectId, k -> new HashSet<>())
.addAll(Arrays.asList(permissions));
}
public void revoke(String subjectId, Permission... permissions) {
Set<Permission> grants = directGrants.get(subjectId);
if (grants != null) {
grants.removeAll(Arrays.asList(permissions));
}
}
public boolean inheritsFromParent() {
// 默认继承,可被配置覆盖
return dataScopeRule == null || dataScopeRule.isInheritEnabled();
}
// 数据范围计算:返回此资源对特定主体的可见数据范围
public DataScope computeDataScope(Subject subject) {
if (dataScopeRule == null) {
return DataScope.all();
}
return dataScopeRule.evaluate(subject, this);
}
}
/**
* 叶子节点:用户/主体
* 在组织架构树中作为叶子,但自身可拥有角色
*/
public class User implements Securable {
private String id;
private String name;
private String username;
private Securable parent; // 所属部门
private Set<Role> directRoles = new HashSet<>();
private boolean enabled;
private LocalDateTime validFrom;
private LocalDateTime validUntil;
@Override
public boolean checkPermission(Subject subject, Permission permission) {
// 用户节点本身不直接承载权限判定逻辑
// 权限通过其角色和所属组织架构间接解析
throw new UnsupportedOperationException("请通过Subject封装调用");
}
@Override
public Set<Permission> resolveEffectivePermissions(Subject subject) {
// 汇总所有角色的权限,考虑角色冲突消解策略
Map<String, Permission> merged = new HashMap<>();
for (Role role : getAllEffectiveRoles()) {
for (Permission p : role.getGlobalPermissions()) {
// 冲突消解:显式拒绝优先
if (p.getEffect() == Effect.DENY) {
merged.put(p.getAction() + ":" + p.getResourcePattern(), p);
} else if (!merged.containsKey(p.getAction() + ":" + p.getResourcePattern())) {
merged.put(p.getAction() + ":" + p.getResourcePattern(), p);
}
}
}
return new HashSet<>(merged.values());
}
// 获取所有生效角色:直接角色 + 继承自组织架构的角色 + 继承自岗位的角色
public Set<Role> getAllEffectiveRoles() {
Set<Role> roles = new HashSet<>(directRoles);
// 从父部门继承
if (parent instanceof Department) {
roles.addAll(((Department) parent).getInheritedRoles());
}
// 从岗位继承
for (Position position : positions) {
roles.addAll(position.getAttachedRoles());
}
return roles;
}
// 其余实现...
}
3.3 复合节点:组织架构与角色树
java
/**
* 复合节点:部门/组织单元
* 可包含子部门、用户、岗位等
*/
public class Department implements Securable {
private String id;
private String name;
private String code; // 组织编码,如"1001.2003.5002"
private DepartmentType type; // GROUP, COMPANY, BRANCH, TEAM等
private Securable parent;
private List<Securable> children = new ArrayList<>();
// 部门级别默认角色(所有成员自动继承)
private Set<Role> defaultRoles = new HashSet<>();
// 权限继承策略
private InheritanceStrategy inheritanceStrategy = InheritanceStrategy.CASCADE;
@Override
public String getId() { return id; }
@Override
public String getName() { return name; }
@Override
public SecurableType getType() { return SecurableType.DEPARTMENT; }
@Override
public Securable getParent() { return parent; }
@Override
public void setParent(Securable parent) { this.parent = parent; }
// ========== 组合核心:子节点管理 ==========
@Override
public void addChild(Securable child) {
// 类型校验
if (!isValidChildType(child.getType())) {
throw new IllegalArgumentException(
"部门不能包含类型为" + child.getType() + "的子节点");
}
// 防止循环引用
if (this.equals(child) || isDescendantOf(child)) {
throw new IllegalArgumentException("不能形成循环引用");
}
// 移除旧父关系
if (child.getParent() != null) {
child.getParent().removeChild(child);
}
children.add(child);
child.setParent(this);
// 触发组织变更事件
publishEvent(new OrgStructureChangedEvent(this, child, ChangeType.ADD));
}
@Override
public void removeChild(Securable child) {
if (children.remove(child)) {
child.setParent(null);
publishEvent(new OrgStructureChangedEvent(this, child, ChangeType.REMOVE));
}
}
@Override
public List<Securable> getChildren() {
// 返回不可修改视图,强制通过组合方法操作
return Collections.unmodifiableList(children);
}
// ========== 权限判定(递归委托) ==========
@Override
public boolean checkPermission(Subject subject, Permission permission) {
// 部门的权限判定:检查是否有子资源满足条件
// 用于"部门管理员"场景:对部门有管理权限即对部门内所有资源有权限
// 1. 检查部门级别的直接授权
if (hasDirectGrant(subject, permission)) {
return true;
}
// 2. 根据继承策略,可能委托给子节点
switch (inheritanceStrategy) {
case CASCADE:
// 级联:任一子节点有权限即认为有权限
return children.stream()
.anyMatch(child -> child.checkPermission(subject, permission));
case STRICT:
// 严格:必须所有子节点都有权限
return children.stream()
.allMatch(child -> child.checkPermission(subject, permission));
case NONE:
// 不继承:仅检查部门自身
return false;
default:
return false;
}
}
@Override
public Set<Permission> resolveEffectivePermissions(Subject subject) {
// 聚合所有子节点的有效权限
Set<Permission> union = new HashSet<>();
// 部门默认角色
for (Role role : defaultRoles) {
if (subject.hasRole(role)) {
union.addAll(role.getGlobalPermissions());
}
}
// 递归聚合子节点
for (Securable child : children) {
union.addAll(child.resolveEffectivePermissions(subject));
}
return union;
}
// ========== 部门特定操作 ==========
/**
* 获取所有子孙用户(扁平化)
*/
public List<User> getAllUsers() {
List<User> users = new ArrayList<>();
preOrder(node -> {
if (node instanceof User) {
users.add((User) node);
}
});
return users;
}
/**
* 获取直接用户(仅一层)
*/
public List<User> getDirectUsers() {
return children.stream()
.filter(c -> c instanceof User)
.map(c -> (User) c)
.collect(Collectors.toList());
}
/**
* 获取所有子部门
*/
public List<Department> getSubDepartments() {
List<Department> depts = new ArrayList<>();
preOrder(node -> {
if (node instanceof Department && node != this) {
depts.add((Department) node);
}
});
return depts;
}
/**
* 计算部门统计:人数、角色分布、权限饱和度等
*/
public DepartmentStatistics computeStatistics() {
DepartmentStatistics stats = new DepartmentStatistics();
postOrder(node -> {
if (node instanceof User) {
stats.incrementUserCount();
stats.mergeRoleDistribution(((User) node).getAllEffectiveRoles());
} else if (node instanceof Department) {
// 后序遍历确保子部门统计已计算
stats.mergeChildStats(((Department) node).computeStatistics());
}
});
return stats;
}
/**
* 批量调整组织架构:移动整个子树
*/
public void moveSubtree(Department targetParent) {
if (this.equals(targetParent) || isDescendantOf(targetParent)) {
throw new IllegalArgumentException("非法的移动操作");
}
Securable oldParent = this.parent;
oldParent.removeChild(this);
targetParent.addChild(this);
// 级联更新编码
regenerateCodes();
publishEvent(new DepartmentMovedEvent(this, oldParent, targetParent));
}
// 辅助方法
private boolean isDescendantOf(Securable ancestor) {
Securable current = this.parent;
while (current != null) {
if (current.equals(ancestor)) return true;
current = current.getParent();
}
return false;
}
private boolean isValidChildType(SecurableType type) {
return type == SecurableType.DEPARTMENT
|| type == SecurableType.USER
|| type == SecurableType.POSITION;
}
private void regenerateCodes() {
String parentCode = parent instanceof Department
? ((Department) parent).getCode()
: "";
this.code = parentCode + "." + id;
// 递归更新所有子部门编码
children.stream()
.filter(c -> c instanceof Department)
.forEach(c -> ((Department) c).regenerateCodes());
}
}
/**
* 复合节点:角色组(角色可继承形成层级)
*/
public class RoleGroup implements Securable {
private String id;
private String name;
private Securable parent; // 父角色或角色组
private List<Securable> children = new ArrayList<>(); // 子角色或子角色组
// 权限规则:此角色组定义的权限模板
private Set<PermissionRule> permissionRules = new HashSet<>();
// 角色冲突消解策略
private ConflictResolution conflictResolution = ConflictResolution.DENY_OVERRIDES;
@Override
public boolean checkPermission(Subject subject, Permission permission) {
// 角色组的权限判定:评估所有规则,应用冲突消解
List<PermissionRule> matchingRules = permissionRules.stream()
.filter(r -> r.matches(permission))
.collect(Collectors.toList());
if (matchingRules.isEmpty() && parent != null) {
// 委托给父角色
return parent.checkPermission(subject, permission);
}
return resolveConflict(matchingRules).stream()
.anyMatch(r -> r.getEffect() == Effect.ALLOW);
}
@Override
public Set<Permission> resolveEffectivePermissions(Subject subject) {
// 递归收集所有子角色的权限,应用冲突消解
Map<String, Permission> merged = new HashMap<>();
// 自身规则
for (PermissionRule rule : permissionRules) {
merged.put(rule.getKey(), rule.toPermission());
}
// 子角色规则(可能覆盖)
for (Securable child : children) {
for (Permission p : child.resolveEffectivePermissions(subject)) {
Permission existing = merged.get(p.getKey());
if (existing == null || shouldOverride(existing, p)) {
merged.put(p.getKey(), p);
}
}
}
return new HashSet<>(merged.values());
}
// 子角色管理
@Override
public void addChild(Securable child) {
if (!(child instanceof Role || child instanceof RoleGroup)) {
throw new IllegalArgumentException("角色组只能包含角色或角色组");
}
children.add(child);
child.setParent(this);
}
@Override
public void removeChild(Securable child) {
children.remove(child);
child.setParent(null);
}
@Override
public List<Securable> getChildren() {
return Collections.unmodifiableList(children);
}
// 其余实现...
}
3.4 迭代器模式扩展:安全遍历
java
/**
* 组合模式的安全迭代器:防止遍历过程中结构修改导致的不一致
*/
public class SafeSecurableIterator implements Iterator<Securable> {
private final Iterator<Securable> snapshotIterator;
public SafeSecurableIterator(Securable root) {
// 创建快照,避免遍历过程中的并发修改
List<Securable> snapshot = new ArrayList<>();
root.preOrder(snapshot::add);
this.snapshotIterator = snapshot.iterator();
}
@Override
public boolean hasNext() {
return snapshotIterator.hasNext();
}
@Override
public Securable next() {
return snapshotIterator.next();
}
}
/**
* 懒加载迭代器:适用于超大树,避免一次性加载全部节点
*/
public class LazySecurableIterator implements Iterator<Securable> {
private final Deque<Iterator<Securable>> stack = new ArrayDeque<>();
public LazySecurableIterator(Securable root) {
stack.push(List.of(root).iterator());
}
@Override
public boolean hasNext() {
while (!stack.isEmpty() && !stack.peek().hasNext()) {
stack.pop();
}
return !stack.isEmpty();
}
@Override
public Securable next() {
if (!hasNext()) throw new NoSuchElementException();
Securable current = stack.peek().next();
// 将子节点迭代器压栈,延迟遍历
if (!current.getChildren().isEmpty()) {
stack.push(current.getChildren().iterator());
}
return current;
}
}
四、组合模式的高级主题
4.1 访问者模式集成
当需要对组合结构执行多种异构操作时,避免在每个节点类中增加方法:
java
public interface SecurableVisitor {
void visitResource(Resource resource);
void visitDepartment(Department department);
void visitUser(User user);
void visitRoleGroup(RoleGroup roleGroup);
}
public class PermissionAuditVisitor implements SecurableVisitor {
private final AuditReport report = new AuditReport();
@Override
public void visitResource(Resource resource) {
// 检查资源是否有孤立授权(授予已离职用户)
for (String subjectId : resource.getDirectGrantSubjects()) {
if (!userService.existsAndActive(subjectId)) {
report.addFinding(new OrphanGrantFinding(resource, subjectId));
}
}
}
@Override
public void visitDepartment(Department department) {
// 检查部门是否形成权限黑洞(子节点权限超过父节点)
// ...
}
// 其余visit方法...
public AuditReport getReport() {
return report;
}
}
// 在Securable接口中增加accept方法
default void accept(SecurableVisitor visitor) {
if (this instanceof Resource) visitor.visitResource((Resource) this);
else if (this instanceof Department) visitor.visitDepartment((Department) this);
// ...
}
4.2 备忘录模式集成:快照与回滚
java
public class OrgStructureMemento {
private final String serializedTree;
private final Instant capturedAt;
public static OrgStructureMemento capture(Securable root) {
return new OrgStructureMemento(serialize(root), Instant.now());
}
public void restore(Securable root) {
// 反序列化并重建树结构
Securable restored = deserialize(serializedTree);
// 替换root的子节点...
}
}
五、组合模式与相关模式的辨析
组合模式 vs 装饰器模式:二者均基于递归组合,但意图迥异。装饰器为对象添加新职责,保持接口不变;组合模式统一处理部分与整体,形成树形结构。装饰器是"增强功能",组合是"统一结构"。
组合模式 vs 享元模式:大组合结构中,大量相似的叶子节点可通过享元共享,减少内存占用。例如文件系统中,大量空文件可共享同一个空内容对象。
组合模式 vs 迭代器模式:组合提供结构,迭代器提供遍历算法。二者常结合使用,以多种顺序(前序、后序、层序)遍历组合结构。
六、现代架构中的组合思想
React组件树:函数组件和类组件形成统一的组件接口,可嵌套组合,props透传实现数据流,正是组合模式的现代前端实践。
Kubernetes资源模型:Pod、ReplicaSet、Deployment、Service等资源对象形成层级组合,kubectl统一操作,无需区分原子资源和复合资源。
Terraform资源配置:模块可嵌套模块,资源可嵌入模块,形成基础设施即代码的组合结构。
权限即代码(RBAC/ABAC):Open Policy Agent的Rego策略、AWS IAM策略,均以组合方式构建复杂的授权规则。
七、设计陷阱与规避策略
陷阱一:组合过深导致性能问题
递归操作在深层组合上可能栈溢出或耗时过长。解决方案:限制组合深度(如组织架构不超过10层)、使用迭代替代递归、或采用路径枚举/嵌套集等数据库优化技术。
陷阱二:透明接口的非法操作
叶子节点调用addChild时抛异常,可能在运行时才暴露问题。解决方案:在开发阶段使用静态分析工具、或提供类型安全的包装方法。
陷阱三:循环引用
父节点误设为自身子孙,导致无限递归。解决方案:在addChild中显式检测,或使用有向图算法验证无环性。
八、结语
组合模式是处理树形层次结构的经典解决方案,其价值在于以一致的接口消解部分与整体的差异,使递归算法得以优雅表达。在权限管理、组织架构、文件系统、UI组件等天然具有层级结构的领域,组合模式是架构设计的基石。理解其透明性设计、掌握与访问者、备忘录等模式的协作、警惕深度与循环等工程陷阱,是运用好这一模式的关键。组合模式不仅是一种代码结构,更是一种认知工具——它教会我们以递归的视角审视世界,在部分与整体的统一中发现简洁之美。