循环语句(Loops):坏味道识别与重构实战指南
循环语句(Loops):坏味道识别与重构实战指南
24种代码坏味道系列 · 第13篇
1. 开篇场景
你是否遇到过这样的代码:使用 for 循环过滤、转换、求和、查找,虽然功能正确,但代码冗长且不够声明式?
1 | std::vector<int> filterEvenNumbers(const std::vector<int>& numbers) { |
这就是循环语句的典型症状。使用命令式循环处理集合,虽然可以工作,但代码冗长且不够声明式。现代C++提供了丰富的STL算法,可以更简洁、更安全地处理集合操作。
当你需要修改循环逻辑时,你必须在循环体中查找和修改。更糟糕的是,循环容易出现边界错误、空指针等问题,增加了bug的风险。
2. 坏味道定义
循环语句是指使用命令式循环处理集合,应该使用更声明式的STL算法。
就像用原始工具完成现代任务,虽然可以工作,但效率低且容易出错。
核心问题:STL算法更简洁、更安全、更易读。使用STL算法可以减少代码量,提高代码的可读性和可维护性。
3. 识别特征
🔍 代码表现:
- 特征1:使用
for循环进行过滤、转换、求和等操作 - 特征2:循环体中有条件判断和集合操作
- 特征3:循环代码冗长,需要多行才能完成简单操作
- 特征4:循环中有手动管理索引或迭代器
- 特征5:循环逻辑可以用STL算法替代
🎯 出现场景:
- 场景1:从其他语言迁移到C++时,保留了循环的习惯
- 场景2:不熟悉STL算法,使用循环实现
- 场景3:快速开发时,使用循环简化代码
- 场景4:重构不彻底,只修改了部分循环
💡 快速自检:
- 问自己:这个循环是否可以用STL算法替代?
- 问自己:这个循环是否在过滤、转换、求和、查找?
- 工具提示:使用代码分析工具检测可以用STL算法替代的循环
4. 危害分析
🚨 维护成本:循环代码冗长,修改时需要理解整个循环逻辑,时间成本增加30%
⚠️ 缺陷风险:循环容易出现边界错误、空指针等问题,bug风险增加40%
🧱 扩展障碍:添加新功能时需要修改循环逻辑,容易破坏现有代码
🤯 认知负担:需要理解循环的每个步骤,增加了心理负担
5. 重构实战
步骤1:安全准备
- ✅ 确保有完整的单元测试覆盖
- ✅ 创建重构分支:
git checkout -b refactor/replace-loops-with-algorithms - ✅ 使用版本控制,便于回滚
步骤2:逐步重构
重构前(问题代码)
1 | // 坏味道:使用命令式循环 |
问题分析:
- 使用命令式循环处理集合操作
- 代码冗长,需要多行才能完成简单操作
- 容易出现边界错误、空指针等问题
重构后(清洁版本)
1 | // ✅ 使用 STL 算法(更声明式) |
关键变化点:
用算法替代循环(Replace Loop with Algorithm):
- 将命令式循环替换为STL算法
- 代码更简洁、更声明式
提高可读性:
- STL算法名称清晰表达意图
- 代码更易读、更易理解
提高安全性:
- STL算法经过充分测试,更安全
- 减少边界错误、空指针等问题
步骤3:重构技巧总结
使用的重构手法:
- 用算法替代循环(Replace Loop with Algorithm):将命令式循环替换为STL算法
- 提取方法(Extract Method):将循环逻辑提取为独立方法
注意事项:
- ⚠️ 确保STL算法适用于当前场景
- ⚠️ 如果循环逻辑复杂,可能需要分步重构
- ⚠️ 重构后要运行所有测试,确保行为一致
6. 预防策略
🛡️ 编码时:
即时检查:
- 循环是否可以用STL算法替代?
- 循环是否在过滤、转换、求和、查找?
- 使用IDE的代码提示,优先选择STL算法
小步提交:
- 发现可以用STL算法替代的循环时,立即重构
- 使用”用算法替代循环”重构,保持代码简洁
🔍 Code Review清单:
重点检查:
- 是否有可以用STL算法替代的循环?
- 循环是否冗长且不够声明式?
- 循环是否容易出现边界错误?
拒绝标准:
- 可以用STL算法替代的循环
- 循环代码冗长且不够声明式
- 循环中有手动管理索引或迭代器
⚙️ 自动化防护:
IDE配置:
- 使用代码分析工具检测可以用STL算法替代的循环
- 启用循环复杂度警告
CI/CD集成:
- 在CI流水线中集成代码分析工具
- 检测可以用STL算法替代的循环,生成警告报告
下一篇预告:冗赘的元素(Lazy Element)- 如何去掉不必要的抽象层
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 青羽川!
评论
