重复代码(Duplicated Code):坏味道识别与重构实战指南
重复代码(Duplicated Code):坏味道识别与重构实战指南
24种代码坏味道系列 · 第2篇
1. 开篇场景
你是否遇到过这样的场景:在 processUsers 方法中写了一段验证逻辑,然后在 processProducts 方法中又写了几乎相同的验证代码?当你需要修改验证规则时,必须在多个地方同步修改,稍有不慎就会遗漏某个地方,导致系统行为不一致。
1 | void processUsers(std::vector<std::string>& users) { |
这就是重复代码的典型症状。相同的代码结构在多个地方出现,就像同一首歌的多个翻唱版本,虽然旋律相同,但每个版本都需要单独维护。
当业务规则发生变化时(比如验证规则从”长度至少3”改为”长度至少5”),你需要在所有重复的地方逐一修改。这不仅浪费时间,更重要的是容易出错——漏掉一个地方,就会导致系统行为不一致,产生难以追踪的bug。
2. 坏味道定义
重复代码是指相同的代码结构在多个地方重复出现,违反了DRY(Don’t Repeat Yourself)原则。
就像同一份文档的多个副本,当需要更新内容时,必须同步更新所有副本,否则就会出现信息不一致的问题。
核心问题:代码重复意味着逻辑重复,任何修改都需要在多个地方同步进行,增加了维护成本和出错概率。
3. 识别特征
🔍 代码表现:
- 特征1:相同的代码块在多个函数/类中出现
- 特征2:只有变量名不同,但逻辑结构完全相同的代码
- 特征3:相似的代码模式反复出现(如相同的错误处理、验证逻辑)
- 特征4:复制粘贴后只修改了少量内容的代码
- 特征5:多个类中有相同的方法实现
🎯 出现场景:
- 场景1:快速开发时,直接复制粘贴现有代码并稍作修改
- 场景2:多个开发者独立实现相似功能,没有代码复用
- 场景3:重构不彻底,只修改了部分重复代码
- 场景4:缺乏代码审查,重复代码没有被及时发现
💡 快速自检:
- 问自己:如果这段逻辑需要修改,我需要在几个地方同时修改?
- 问自己:这段代码是否在其他地方出现过?
- 工具提示:使用
grep或 IDE 的”查找相似代码”功能,可以快速发现重复模式
4. 危害分析
🚨 维护成本:修改一处逻辑需要在多处同步,时间成本增加2-3倍
⚠️ 缺陷风险:容易遗漏某些地方的修改,导致bug,风险增加60%
🧱 扩展障碍:新功能开发时不知道应该复用哪段代码,容易产生新的重复
🤯 认知负担:需要记住哪些地方有重复代码,增加了心理负担
5. 重构实战
步骤1:安全准备
- ✅ 确保有完整的单元测试覆盖,特别是涉及重复代码的功能
- ✅ 创建重构分支:
git checkout -b refactor/extract-common-logic - ✅ 使用版本控制,便于回滚
步骤2:逐步重构
重构前(问题代码)
1 | class BadExample { |
问题分析:
processUsers和processProducts中有完全相同的验证逻辑- 如果验证规则改变(如最小长度改为5),需要在两个地方修改
- 错误消息中的”user name”和”product name”可以统一为”name”
重构后(清洁版本)
1 | class GoodExample { |
关键变化点:
提取方法(Extract Method):
- 将重复的验证逻辑提取到
validateName方法中 - 统一错误消息,使用通用的”name”而不是”user name”或”product name”
- 将重复的验证逻辑提取到
简化调用:
- 两个方法都调用
validateName,代码更简洁 - 验证逻辑集中管理,修改时只需改一处
- 两个方法都调用
提高可维护性:
- 如果需要修改验证规则(如最小长度改为5),只需修改
validateName方法 - 如果需要添加新的验证规则(如不能包含特殊字符),也只需修改一处
- 如果需要修改验证规则(如最小长度改为5),只需修改
步骤3:重构技巧总结
使用的重构手法:
- 提取方法(Extract Method):将重复的代码块提取为独立方法
- 统一命名(Rename):将相似的命名统一,便于识别重复
注意事项:
- ⚠️ 提取方法时,确保参数命名足够通用,适用于所有使用场景
- ⚠️ 如果重复代码有细微差异,先统一差异,再提取
- ⚠️ 提取后要运行所有测试,确保行为没有改变
6. 预防策略
🛡️ 编码时:
即时检查:
- 复制粘贴代码后,立即问自己:这段代码是否可以提取为公共方法?
- 编写新功能时,先检查是否有类似的现有代码可以复用
- 使用IDE的”查找相似代码”功能,及时发现重复
小步提交:
- 每次提交前检查是否有重复代码
- 如果发现重复,立即重构后再提交
🔍 Code Review清单:
重点检查:
- 新代码是否与现有代码重复?
- 是否有可以提取的公共逻辑?
- 错误处理和验证逻辑是否统一?
拒绝标准:
- 明显的复制粘贴代码(超过5行相同)
- 可以复用但没有复用的代码
- 多个地方有相同业务规则但实现不同
⚙️ 自动化防护:
IDE配置:
- 启用代码重复检测插件(如 CPD - Copy/Paste Detector)
- 配置相似代码阈值(如超过10行相同代码时警告)
CI/CD集成:
- 在CI流水线中集成代码重复检测工具(如
jscpd、PMD CPD) - 设置重复代码阈值,超过阈值时阻止合并
- 定期生成代码重复报告,识别需要重构的区域
- 在CI流水线中集成代码重复检测工具(如
下一篇预告:过长函数(Long Function)- 如何将”巨无霸”函数拆分为可管理的小函数
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 青羽川!
评论
