霰弹式修改(Shotgun Surgery):坏味道识别与重构实战指南
霰弹式修改(Shotgun Surgery):坏味道识别与重构实战指南
24种代码坏味道系列 · 第8篇
1. 开篇场景
你是否遇到过这样的场景:需要修改用户验证规则(如最小长度从3改为5),却发现需要在 UserService、UserController、UserRepository 等多个类中逐一修改相同的验证逻辑?
1 | // 需要在多个类中修改相同的验证逻辑 |
这就是霰弹式修改的典型症状。一个变化需要在多个类中修改,就像用霰弹枪打靶,需要开很多枪才能命中目标。每次修改都需要在多个地方同步进行,容易遗漏某个地方,导致系统行为不一致。
当你需要修改业务规则时(如验证规则、计算逻辑),你必须在多个类中逐一修改。这不仅浪费时间,更重要的是容易出错——漏掉一个地方,就会导致系统行为不一致,产生难以追踪的bug。
2. 坏味道定义
霰弹式修改是指一个变化需要在多个类中修改,违反了开闭原则(对扩展开放,对修改关闭)。
就像同一首歌的多个翻唱版本,当需要修改歌词时,必须同步更新所有版本,否则就会出现不一致。
核心问题:相关的逻辑应该集中在一个地方。如果相同的逻辑分散在多个类中,修改时需要同步修改多个地方,增加了维护成本和出错概率。
3. 识别特征
🔍 代码表现:
- 特征1:相同的逻辑在多个类中重复出现
- 特征2:修改某个业务规则时,需要在多个类中修改
- 特征3:多个类中有相似的代码模式
- 特征4:类的职责不清晰,包含了不属于它的逻辑
- 特征5:修改一个功能需要修改多个不相关的类
🎯 出现场景:
- 场景1:快速开发时,在多个类中复制粘贴相同的逻辑
- 场景2:重构不彻底,只修改了部分重复代码
- 场景3:缺乏设计,没有考虑代码复用
- 场景4:业务规则变化时,在多个地方添加相同的逻辑
💡 快速自检:
- 问自己:如果这个业务规则需要修改,我需要在几个类中修改?
- 问自己:这些类中是否有相同的逻辑可以提取?
- 工具提示:使用
grep或 IDE 的”查找相似代码”功能,可以快速发现重复模式
4. 危害分析
🚨 维护成本:修改一个业务规则需要在多个类中同步修改,时间成本增加2-3倍
⚠️ 缺陷风险:容易遗漏某些地方的修改,导致bug,风险增加70%
🧱 扩展障碍:添加新功能时不知道应该在哪里修改,容易产生新的重复
🤯 认知负担:需要记住哪些类中有相同的逻辑,增加了心理负担
5. 重构实战
步骤1:安全准备
- ✅ 确保有完整的单元测试覆盖
- ✅ 创建重构分支:
git checkout -b refactor/consolidate-duplicate-logic - ✅ 使用版本控制,便于回滚
步骤2:逐步重构
重构前(问题代码)
1 | // 坏味道:如果要改变用户验证逻辑,需要在多个地方修改 |
问题分析:
- 验证逻辑分散在3个不同的类中
- 如果验证规则改变(如最小长度改为5),需要在3个地方修改
- 验证逻辑可能不一致(错误消息不同)
重构后(清洁版本)
1 | // ✅ 集中验证逻辑 |
关键变化点:
提取类(Extract Class):
- 将验证逻辑提取到
UserValidationRules类中 - 所有类都使用同一个验证规则
- 将验证逻辑提取到
集中管理:
- 验证规则集中在一个地方
- 修改验证规则时,只需修改
UserValidationRules
统一行为:
- 所有类使用相同的验证逻辑
- 确保系统行为一致
步骤3:重构技巧总结
使用的重构手法:
- 提取类(Extract Class):将分散的逻辑提取到专门的类中
- 移动方法(Move Method):将方法移到合适的类中
- 内联方法(Inline Method):将重复的方法调用替换为统一的调用
注意事项:
- ⚠️ 确保提取的类有清晰的职责
- ⚠️ 如果逻辑有细微差异,先统一差异,再提取
- ⚠️ 重构后要运行所有测试,确保行为一致
6. 预防策略
🛡️ 编码时:
即时检查:
- 发现相同的逻辑在多个类中出现时,立即提取
- 使用IDE的”查找相似代码”功能,及时发现重复
- 编写新功能时,先检查是否有类似的现有代码可以复用
小步提交:
- 每次提交前检查是否有重复逻辑
- 如果发现重复,立即重构后再提交
🔍 Code Review清单:
重点检查:
- 新代码是否与现有代码重复?
- 是否有可以提取的公共逻辑?
- 业务规则是否集中管理?
拒绝标准:
- 相同的逻辑在多个类中重复
- 修改业务规则需要在多个类中修改
- 可以复用但没有复用的代码
⚙️ 自动化防护:
IDE配置:
- 启用代码重复检测插件
- 配置相似代码阈值
CI/CD集成:
- 在CI流水线中集成代码重复检测工具
- 设置重复代码阈值,超过阈值时阻止合并
- 定期生成代码重复报告
下一篇预告:依恋情结(Feature Envy)- 如何让方法待在它应该待的地方
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 青羽川!
评论
