名称: 组件扁平化分析 描述: 通过检测根命名空间中的孤儿类并确保组件仅作为叶子节点,识别并修复组件层次结构问题。使用场景包括分析组件结构、查找孤儿类、扁平化组件层次结构、移除组件嵌套,或当用户询问组件扁平化、孤儿类或组件结构清理时。
组件扁平化分析
这个技能识别组件层次结构问题,并确保组件在目录/命名空间结构中仅作为叶子节点存在,移除根命名空间中的孤儿类。
如何使用
快速开始
请求分析您的代码库:
- “查找根命名空间中的孤儿类”
- “扁平化组件层次结构”
- “识别需要扁平化的组件”
- “分析组件结构以查找层次结构问题”
使用示例
示例1:查找孤儿类
用户:“查找根命名空间中的孤儿类”
技能将:
1. 扫描组件命名空间以查找层次结构问题
2. 识别根命名空间中的孤儿类
3. 检测基于其他组件构建的组件
4. 建议扁平化策略
5. 创建重构计划
示例2:扁平化组件
用户:“扁平化此代码库中的组件层次结构”
技能将:
1. 识别有层次结构问题的组件
2. 分析孤儿类
3. 建议合并或拆分策略
4. 创建重构计划
5. 估算工作量
示例3:组件结构分析
用户:“分析组件结构以查找层次结构问题”
技能将:
1. 映射组件命名空间结构
2. 识别有代码的根命名空间
3. 查找基于组件构建的组件
4. 标记层次结构违规
5. 提供建议
逐步过程
- 扫描结构:映射组件命名空间层次结构
- 识别问题:查找孤儿类和组件嵌套
- 分析选项:确定扁平化策略(合并 vs 拆分)
- 创建计划:生成步骤化的重构计划
- 执行:重构组件以移除层次结构
使用时机
应用此技能当:
- 在收集通用领域组件后(模式2)
- 在确定组件依赖之前(模式4)
- 当组件有嵌套结构时
- 查找根命名空间中的孤儿类时
- 准备领域分组时
- 清理组件结构时
- 确保组件仅作为叶子节点时
核心概念
组件定义
组件由目录/命名空间结构中的叶子节点识别:
- 叶子节点:包含源文件的最深层目录
- 组件:叶子节点命名空间中的源代码文件
- 子域:已被扩展的父命名空间
关键规则:组件仅作为叶子节点存在。如果一个命名空间被扩展,父节点成为子域,而不是组件。
根命名空间
根命名空间是一个已被扩展的命名空间节点:
- 扩展:另一个命名空间节点添加在其下方
- 示例:
ss.survey扩展为ss.survey.templates - 结果:
ss.survey成为根命名空间(子域)
孤儿类
孤儿类是根命名空间中的源文件:
- 位置:根命名空间(非叶子节点)
- 问题:没有可定义的组件与之关联
- 解决方案:移动到叶子节点命名空间(组件)
示例:
ss.survey/ ← 根命名空间(被.templates扩展)
├── Survey.js ← 孤儿类(在根命名空间中)
└── templates/ ← 组件(叶子节点)
└── Template.js
扁平化策略
策略1:向下合并
- 将代码从叶子节点移动到根命名空间
- 使根命名空间成为组件
- 示例:移动
ss.survey.templates→ss.survey
策略2:向上拆分
- 将代码从根命名空间移动到新的叶子节点
- 从根命名空间创建新组件
- 示例:拆分
ss.survey→ss.survey.create+ss.survey.process
策略3:移动共享代码
- 将共享代码移动到专用组件
- 创建
.shared组件 - 示例:
ss.survey共享代码 →ss.survey.shared
分析过程
阶段1:映射组件结构
扫描目录/命名空间结构以识别层次结构:
-
映射命名空间树
- 构建所有命名空间的树
- 识别父子关系
- 标记叶子节点(组件)
-
识别根命名空间
- 查找已被扩展的命名空间
- 标记为根命名空间(子域)
- 注意哪些命名空间扩展了它们
-
定位源文件
- 查找每个命名空间中的所有源文件
- 映射文件到其命名空间位置
- 识别根命名空间中的文件
示例结构映射:
## 组件结构映射
ss.survey/ ← 根命名空间(已扩展)
├── Survey.js ← 孤儿类
├── SurveyProcessor.js ← 孤儿类
└── templates/ ← 组件(叶子节点)
├── EmailTemplate.js
└── SMSTemplate.js
ss.ticket/ ← 根命名空间(已扩展)
├── Ticket.js ← 孤儿类
├── assign/ ← 组件(叶子节点)
│ └── TicketAssign.js
└── route/ ← 组件(叶子节点)
└── TicketRoute.js
阶段2:识别孤儿类
查找根命名空间中的源文件:
-
扫描根命名空间
- 检查每个根命名空间是否有源文件
- 识别孤儿文件
- 统计每个根命名空间的孤儿文件数
-
分类孤儿类
- 共享代码:常见工具、接口、抽象类
- 领域代码:应位于组件中的业务逻辑
- 混合:共享和领域代码的组合
-
评估影响
- 有多少文件是孤儿的?
- 它们包含什么功能?
- 哪些组件依赖它们?
示例孤儿类检测:
## 发现的孤儿类
### 根命名空间:ss.survey
**孤儿文件**(5个文件):
- Survey.js(领域代码 - 调查创建)
- SurveyProcessor.js(领域代码 - 调查处理)
- SurveyValidator.js(共享代码 - 验证)
- SurveyFormatter.js(共享代码 - 格式化)
- SurveyConstants.js(共享代码 - 常量)
**分类**:
- 领域代码:2个文件(应位于组件中)
- 共享代码:3个文件(应位于.shared组件中)
**依赖**:被ss.survey.templates组件使用
阶段3:分析扁平化选项
为每个根命名空间确定最佳扁平化策略:
-
选项1:向下合并
- 将叶子节点代码移动到根命名空间
- 使根命名空间成为组件
- 使用当:叶子节点小、功能相关时
-
选项2:向上拆分
- 将根命名空间代码移动到新的叶子节点
- 从根创建多个组件
- 使用当:根命名空间有不同功能区域时
-
选项3:移动共享代码
- 提取共享代码到
.shared组件 - 保留领域代码在根或拆分
- 使用当:根命名空间有共享工具时
- 提取共享代码到
示例扁平化分析:
## 扁平化选项分析
### 根命名空间:ss.survey
**当前状态**:
- 根命名空间:5个孤儿文件
- 叶子组件:ss.survey.templates(7个文件)
**选项1:向下合并** ✅ 推荐
- 将模板代码移动到ss.survey
- 结果:单一组件ss.survey
- 工作量:低(移动7个文件)
- 理由:模板小,与调查功能相关
**选项2:向上拆分**
- 创建ss.survey.create(2个文件)
- 创建ss.survey.process(1个文件)
- 创建ss.survey.shared(3个文件)
- 保留ss.survey.templates(7个文件)
- 工作量:高(创建多个组件)
- 理由:更细粒度,但可能过度工程
**选项3:移动共享代码**
- 创建ss.survey.shared(3个共享文件)
- 保留领域代码在根(2个文件)
- 保留ss.survey.templates(7个文件)
- 工作量:中等
- 理由:分离共享和领域,但仍存在层次结构
阶段4:创建扁平化计划
为每个根命名空间生成重构计划:
-
选择策略
- 选择最佳扁平化选项
- 考虑工作量、复杂性、可维护性
-
规划重构步骤
- 列出要移动的文件
- 识别目标命名空间
- 注意要更新的依赖
-
估算工作量
- 重构时间
- 风险评估
- 测试要求
示例扁平化计划:
## 扁平化计划
### 优先级:高
**根命名空间:ss.survey**
**策略**:向下合并
**步骤**:
1. 将文件从ss.survey.templates/移动到ss.survey/
- EmailTemplate.js
- SMSTemplate.js
- [5个其他文件]
2. 更新依赖组件的导入
- 更新引用从ss.survey.templates._到ss.survey._
3. 移除ss.survey.templates/目录
4. 更新命名空间声明
- 将命名空间从ss.survey.templates更改为ss.survey
5. 运行测试以验证更改
**工作量**:2-3天
**风险**:低(模板是自包含的)
**依赖**:无
阶段5:执行扁平化
执行重构:
-
移动文件
- 移动源文件到目标命名空间
- 更新文件路径和导入
-
更新引用
- 更新依赖组件的导入
- 更新命名空间声明
- 更新目录结构
-
验证更改
- 运行测试
- 检查损坏的引用
- 验证组件结构
输出格式
孤儿类报告
## 孤儿类分析
### 根命名空间:ss.survey
**状态**:⚠️ 有孤儿类
**孤儿文件**(5个文件):
- Survey.js(领域代码)
- SurveyProcessor.js(领域代码)
- SurveyValidator.js(共享代码)
- SurveyFormatter.js(共享代码)
- SurveyConstants.js(共享代码)
**叶子组件**:
- ss.survey.templates(7个文件)
**问题**:根命名空间包含代码但被叶子组件扩展
**建议**:将模板合并到根命名空间
组件层次结构问题
## 组件层次结构问题
| 根命名空间 | 孤儿文件 | 叶子组件 | 问题 | 建议 |
| ---------- | -------- | ------------------------ | ------------------- | ----------- |
| ss.survey | 5 | 1(templates) | 有孤儿类 | 向下合并 |
| ss.ticket | 45 | 2(assign, route) | 孤儿代码多 | 向上拆分 |
| ss.reporting | 0 | 3(tickets, experts, financial) | 无问题 | ✅ OK |
扁平化计划
## 扁平化计划
### 优先级:高
**ss.survey** → 向下合并
- 移动7个文件从templates到根
- 工作量:2-3天
- 风险:低
### 优先级:中等
**ss.ticket** → 向上拆分
- 创建ss.ticket.maintenance(30个文件)
- 创建ss.ticket.completion(10个文件)
- 创建ss.ticket.shared(5个文件)
- 工作量:1周
- 风险:中等
分析清单
结构映射:
- [ ] 映射所有命名空间层次结构
- [ ] 识别根命名空间
- [ ] 定位所有源文件
- [ ] 标记叶子节点(组件)
孤儿类检测:
- [ ] 扫描根命名空间以查找源文件
- [ ] 识别孤儿类
- [ ] 分类孤儿类(共享/领域/混合)
- [ ] 评估影响和依赖
扁平化分析:
- [ ] 分析合并选项
- [ ] 分析拆分选项
- [ ] 分析共享代码提取选项
- [ ] 为每个根命名空间选择最佳策略
计划创建:
- [ ] 选择扁平化策略
- [ ] 创建重构步骤
- [ ] 估算工作量和风险
- [ ] 优先级排序工作
执行:
- [ ] 移动文件到目标命名空间
- [ ] 更新导入和引用
- [ ] 更新命名空间声明
- [ ] 通过测试验证更改
实现说明
对于Node.js/Express应用
组件通常在services/目录中:
services/
├── survey/ ← 根命名空间(已扩展)
│ ├── Survey.js ← 孤儿类
│ └── templates/ ← 组件(叶子节点)
│ └── Template.js
扁平化:
- 合并:移动
templates/文件到survey/ - 拆分:创建
survey/create/和survey/process/ - 共享:创建
survey/shared/用于工具
对于Java应用
组件通过包结构识别:
com.company.survey ← 根包(已扩展)
├── Survey.java ← 孤儿类
└── templates/ ← 组件(叶子包)
└── Template.java
扁平化:
- 合并:移动
templates类到survey包 - 拆分:创建
survey.create和survey.process包 - 共享:创建
survey.shared包
检测策略
查找有代码的根命名空间:
// 查找包含源文件的根命名空间
function findRootNamespacesWithCode(namespaces, sourceFiles) {
const rootNamespaces = namespaces.filter((ns) => {
// 检查命名空间是否被扩展
const hasChildren = namespaces.some((n) => n.startsWith(ns + '.') || n.startsWith(ns + '/'))
// 检查命名空间是否包含源文件
const hasFiles = sourceFiles.some((f) => f.namespace === ns)
return hasChildren && hasFiles
})
return rootNamespaces
}
查找孤儿类:
// 查找根命名空间中的孤儿类
function findOrphanedClasses(rootNamespaces, sourceFiles) {
const orphaned = []
rootNamespaces.forEach((rootNs) => {
const files = sourceFiles.filter((f) => f.namespace === rootNs)
orphaned.push({
rootNamespace: rootNs,
files: files,
count: files.length,
})
})
return orphaned
}
适应性函数
扁平化组件后,创建自动化检查:
根命名空间中无源代码
// 如果根命名空间中存在源代码,则发出警报
function checkRootNamespaceCode(namespaces, sourceFiles) {
const violations = []
namespaces.forEach((ns) => {
// 检查命名空间是否被扩展
const hasChildren = namespaces.some((n) => n.startsWith(ns + '.') || n.startsWith(ns + '/'))
if (hasChildren) {
// 检查命名空间是否包含源文件
const files = sourceFiles.filter((f) => f.namespace === ns)
if (files.length > 0) {
violations.push({
namespace: ns,
files: files.map((f) => f.name),
issue: '根命名空间包含源文件(孤儿类)',
})
}
}
})
return violations
}
组件仅作为叶子节点
// 确保组件仅作为叶子节点存在
function validateComponentStructure(namespaces, sourceFiles) {
const violations = []
// 查找所有叶子节点(组件)
const leafNodes = namespaces.filter((ns) => {
return !namespaces.some((n) => n.startsWith(ns + '.') || n.startsWith(ns + '/'))
})
// 检查所有源文件是否在叶子节点中
sourceFiles.forEach((file) => {
if (!leafNodes.includes(file.namespace)) {
violations.push({
file: file.name,
namespace: file.namespace,
issue: '源文件不在叶子节点(组件)中',
})
}
})
return violations
}
最佳实践
应做 ✅
- 确保组件仅作为叶子节点存在
- 移除根命名空间中的孤儿类
- 基于功能选择扁平化策略
- 功能相关时合并
- 功能不同时拆分
- 提取共享代码到
.shared组件 - 扁平化后更新所有引用
- 通过测试验证更改
不应做 ❌
- 不要在根命名空间中留下孤儿类
- 不要在其他组件之上创建组件
- 不要跳过移动文件后更新导入
- 不要在不分析影响的情况下扁平化
- 不要不一致地混合扁平化策略
- 不要扁平化时忽略共享代码
- 不要跳过重构后测试
常见模式
模式1:简单合并
之前:
ss.survey/
├── Survey.js ← 孤儿
└── templates/ ← 组件
└── Template.js
之后:
ss.survey/ ← 组件(叶子节点)
├── Survey.js
└── Template.js
模式2:功能拆分
之前:
ss.ticket/ ← 根命名空间
├── Ticket.js ← 孤儿(45个文件)
├── assign/ ← 组件
└── route/ ← 组件
之后:
ss.ticket/ ← 子域
├── maintenance/ ← 组件
│ └── Ticket.js
├── completion/ ← 组件
│ └── TicketCompletion.js
├── assign/ ← 组件
└── route/ ← 组件
模式3:共享代码提取
之前:
ss.survey/ ← 根命名空间
├── Survey.js ← 领域代码
├── SurveyValidator.js ← 共享代码
└── templates/ ← 组件
之后:
ss.survey/ ← 组件
├── Survey.js
└── shared/ ← 组件
└── SurveyValidator.js
后续步骤
扁平化组件后:
- 应用确定组件依赖模式 - 分析耦合
- 创建组件域 - 将组件分组到域中
- 创建域服务 - 提取域到服务
注意
- 组件必须仅作为叶子节点存在
- 有代码的根命名空间是问题所在
- 扁平化提高组件清晰度
- 基于功能选择扁平化策略
- 共享代码应位于专用组件中
- 移动文件后始终更新引用
- 扁平化后彻底测试