首页/开发/代码质量与评审速查/手法:Split Loop(拆分循环)
重构手法62 条

手法:Split Loop(拆分循环)

一个循环同时做多件事时拆成多个单职责循环:可读性与可提炼性提升,现代编译器与运行时常常能抵消遍历开销;数据量大时先度量再决定。

是什么

拆分循环(Split Loop)把一个同时做多件事的循环拆成多个各自只做一件事的循环。好处是每段循环可以用一句话命名,容易被提炼成独立函数,也更容易单独优化。代价是遍历次数增加,现代运行时对简单遍历的优化通常能抵消这部分开销,但数据量很大或循环体内是重计算时,需要先度量再决定。

要点

信号循环体内同时做两件以上互不相关的计算
做法按职责拆环,各自提炼成具名函数
权衡遍历次数增加,热路径上必须先做性能评估
反向考虑只是同一件聚合的两步时,保持单循环更清晰

典型使用场景

  • 统计与筛选混在一趟循环:先算最小值又累加求和,两件事分别拆开可读性明显提升
  • 循环内既更新状态又收集结果:拆分后便于对收集部分单独写测试
  • 准备提炼函数:一整个多职责循环很难命名,拆开后每段都能叫出名字
  • 多趟处理便于并行或向量化:拆开后某些段可直接换成库函数
  • 循环体过长导致难调试:拆成几段后可以逐段打断点定位

示例

示例
// 调整前:一趟循环算两个无关的值
for (Person p : people) {
    youngest = Math.min(youngest, p.age());
    total += p.salary();
}

// 调整后:按职责拆分,各自可命名、可提炼
for (Person p : people) youngest = Math.min(youngest, p.age());
for (Person p : people) total += p.salary();

拆与不拆的判断

情形建议原因
集合规模小且代码清晰度受益拆遍历开销可忽略,可读性优先
循环在热路径、集合很大先度量再决定遍历翻倍可能影响吞吐,需要基准数据
循环体包含远程调用或写操作谨慎拆开后调用次数与写入顺序都可能变化
两段逻辑共享中间计算结果不拆或先提取共享部分拆开会导致重复计算,得不偿失
循环体只有一步聚合的两小段保留单循环拆开后收益有限,反而增加代码量
拆分时要连带处理循环控制语句:循环体内的提前返回、跳出与异常处理在拆分后语义会变,需要逐条确认行为一致。
在支持流式写法的语言里,拆开的循环常常可以直接改写成过滤、映射、归约的组合表达式,进一步消除显式循环带来的噪声。

落地检查清单

  • 拆分后每段循环都能用一句话说清在算什么
  • 对性能敏感的路径补一组基准数据作为依据
  • 检查循环控制语句在拆分后的语义是否等价
  • 确认没有为了拆而拆:两段逻辑相关性高时保持原样

易错点

  • 对热路径直接拆环不评估:遍历次数成倍增加,吞吐下降且原因不易第一时间定位
  • 拆完仍是复杂聚合:反而比原来更难读懂,说明该先提炼聚合逻辑而不是拆循环
  • 忽略循环控制语句:把带提前返回的循环拆开,后续段在本该跳出的情况下继续执行
  • 为了形式统一而拆:小集合上收益为零,代码行增多,后续维护成本上升

数据来源与参考

  • 《重构》第 2 版 · 第 8 章(Split Loop)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌