发散式变化(Divergent Change):坏味道识别与重构实战指南
发散式变化(Divergent Change):坏味道识别与重构实战指南
24种代码坏味道系列 · 第7篇
1. 开篇场景
你是否遇到过这样的类:当数据库格式改变时,需要修改它;当XML格式改变时,需要修改它;当计算逻辑改变时,也需要修改它?一个类因为多种不同的原因需要修改,就像一台多功能机器,任何一个功能出问题都需要整台机器停下来维修。
1 | class BadExample { |
这就是发散式变化的典型症状。一个类承担了太多职责,违反了单一职责原则。当某个职责的需求发生变化时,你必须在同一个类中修改,即使这个变化与其他职责无关。
当你需要修改序列化格式时,你可能会意外影响计算逻辑。当你需要修改计算逻辑时,你可能会意外影响显示格式。这种耦合使得代码变得脆弱,任何修改都可能产生意想不到的副作用。
2. 坏味道定义
发散式变化是指一个类因为不同的原因需要修改,违反了单一职责原则。
就像一个多功能的瑞士军刀,虽然功能齐全,但任何一个功能损坏都需要整把刀维修。
核心问题:一个类应该只有一个修改的理由。如果类因为多种原因需要修改,说明它承担了太多职责,应该拆分。
3. 识别特征
🔍 代码表现:
- 特征1:类中有多个不相关的功能(如序列化、计算、显示)
- 特征2:修改某个功能时,需要修改同一个类的多个方法
- 特征3:类的注释中提到了多个职责(如”负责序列化和计算”)
- 特征4:类名使用了”and”连接多个概念(如
EmployeeSerializerAndCalculator) - 特征5:类的不同方法处理不同的关注点
🎯 出现场景:
- 场景1:快速开发时,将所有相关功能放在一个类中
- 场景2:重构不彻底,只修改了部分代码
- 场景3:需求变更时,不断在现有类中添加新功能
- 场景4:缺乏设计,没有考虑单一职责原则
💡 快速自检:
- 问自己:这个类是否因为多个不同的原因需要修改?
- 问自己:如果删除这个类的某个功能,类名是否仍然准确?
- 工具提示:使用代码度量工具检测类的职责数量
4. 危害分析
🚨 维护成本:修改某个功能时需要理解整个类,时间成本增加50%
⚠️ 缺陷风险:修改一个功能可能影响其他功能,bug风险增加60%
🧱 扩展障碍:添加新功能时不知道应该放在哪里,容易破坏现有结构
🤯 认知负担:需要理解所有职责才能修改,心理负担重
5. 重构实战
步骤1:安全准备
- ✅ 确保有完整的单元测试覆盖
- ✅ 创建重构分支:
git checkout -b refactor/extract-class - ✅ 使用版本控制,便于回滚
步骤2:逐步重构
重构前(问题代码)
1 | class BadExample { |
问题分析:
- 类承担了4个不同的职责:数据存储、序列化、计算、显示
- 每个职责的变化都需要修改同一个类
- 职责之间没有清晰的边界
重构后(清洁版本)
1 | // ✅ 核心数据类,只负责数据存储 |
关键变化点:
提取类(Extract Class):
- 将不同职责提取到不同的类中
- 每个类只负责一个职责
单一职责:
Employee只负责数据存储EmployeeSerializer只负责序列化TaxCalculator只负责计算EmployeeDisplay只负责显示
提高可维护性:
- 修改序列化格式时,只需修改
EmployeeSerializer - 修改计算逻辑时,只需修改
TaxCalculator - 职责清晰,互不影响
- 修改序列化格式时,只需修改
步骤3:重构技巧总结
使用的重构手法:
- 提取类(Extract Class):将不同职责提取到不同的类中
- 移动方法(Move Method):将方法移到合适的类中
注意事项:
- ⚠️ 确保每个类有清晰的职责边界
- ⚠️ 如果类之间有依赖,使用组合而不是继承
- ⚠️ 重构后要更新所有使用处,确保行为一致
6. 预防策略
🛡️ 编码时:
即时检查:
- 类是否只负责一个职责?
- 类名是否使用了”and”连接多个概念?
- 修改某个功能时,是否需要修改类的其他部分?
小步提交:
- 发现类承担多个职责时,立即拆分
- 使用”提取类”重构,保持职责单一
🔍 Code Review清单:
重点检查:
- 类是否因为多个原因需要修改?
- 类的方法是否处理不同的关注点?
- 是否可以拆分为更小的类?
拒绝标准:
- 类名包含”and”连接多个概念
- 类的方法处理不相关的功能
- 修改某个功能需要理解整个类
⚙️ 自动化防护:
IDE配置:
- 使用代码度量工具检测类的职责数量
- 启用类复杂度警告
CI/CD集成:
- 在CI流水线中集成代码度量工具
- 设置类的职责数量阈值,超过阈值时生成警告
下一篇预告:霰弹式修改(Shotgun Surgery)- 如何集中分散的修改点
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 青羽川!
评论
