在组合模式的Unity示例中, 核心遵循了一些设计原则, 但也因模式特性的存在对某一原则的”妥协”(并非结对违反, 而是为了模式目标的合理取舍).
一、遵循的设计原则
- 单一职责原则(SRP)
体现:叶子节点和复合节点各自承担单一职责。
- 叶子节点(如
UIButton\UIText)仅负责自身的基础行为 (显示\隐藏), 不关心子元素管理(因为它们没有子元素)
- 复合节点(如
UIPanel) 除了自身的显示\隐藏, 还负责子元素的管理(添加\移除\递归操作子节点), 职责聚焦于 “组合管理”.
两者职责边界清晰, 修改叶子节点的显示逻辑不会影响复合节点的子元素管理,反之亦然.
- 开放 - 封闭原则(OCP)
体现: 系统对扩展开放, 对修改封闭
- 新增叶子节点(如
UIIcon图标): 只需继承UIComponent并实现Show/Hide, 无需修改现有UIButton\UIPanel等代码.
- 新增复合节点(如
UIScrollPanel滚动面板): 只需继承UIComponent, 重写自元素管理方法和递归逻辑, 现有代码无需改动.
- 客户端(如
UIManager) 调用的是抽象UIComponent接口, 新增元素后客户端代码无需修改.
- 依赖倒置原则(DIP)
体现: 客户端依赖抽象, 而非具体实现.
- 客户端
UIComponent.Show() / Hide()统一调用, 无需关心具体类型.这种依赖抽象的设计,让客户端与具体元素解耦,支持任意扩展.
二、因模式特性”妥协”的设计原则(非绝对违反)
组合模式的核心是”让叶子和复合节点对外暴露统一接口”, 这回导致对接口隔离原则(ISP)的一定程度的”违背“ (或称为“必要妥协”)
接口隔离原则(ISP)的核心思想:
客户端不应该依赖他不需要的接口; 一个类对另一个类的依赖应建立在最小的接口上.
组合模式中的”妥协”:
在示例中, 抽象组件
UIComponent 定义了所有方法, 包括叶子节点不需要的子元素管理方法 (AddChild \ RemoveChild 等).- 叶子节点(如
UIbutton)不需要管理子元素, 但为了实现UIComponent接口, 必须空实现这些方法(否则会有报错).
- 这导致叶子节点依赖了自身不需要的接口方法, 一定程度上违反了”最小接口依赖”的原则
为什么是”必要妥协”?
组合模式的核心价值是”统一处理叶子和复合节点”. 如果为叶子和复合节点分别定义接口(如
ILeaf 和 IComposite ), 客户端就需要区分两种接口, 无法实现”统一操作”的目标.因此, 这种对接口隔离原则的妥协, 是为了换取”树形结构统一处理”的核心又是, 属于模式设计中的合理取舍.
总结
组合模式主要遵循单一职责原则(分离叶子与复合节点的职责)\ 开放-封闭原则(支持独立扩展)\ 依赖导致原则(客户端依赖抽象); 但为了实现”统一接口”的核心目标, 会对接口隔离原则做出必要妥协(叶子节点被迫实现不需要的方法). 这种妥协是模式设计的权衡结果, 而非错误.