首页/开发/代码质量与评审速查/手法:Extract Function(提炼函数)
重构手法62 条

手法:Extract Function(提炼函数)

把一段可命名的逻辑取出为独立函数:用意图命名替代「这段是干什么的」注释,缩小函数体、暴露复用点;片段内局部变量越少,提炼越顺。

这个手法做什么

Extract Function(提炼函数)把一段能命名的逻辑取出来,放进一个以意图命名的函数里。它的直接收益是用函数名替代「这段在干什么」的注释,让外层函数变短、层次变清晰;片段一旦独立,也更容易被复用和单独测试。Fowler 给的判断很直接:如果必须花力气才能看懂一个片段在做什么,就把它提炼出来并按意图命名。

基本信息

适用场景长函数里的独立片段、需要写注释解释的代码块
命名原则按做什么命名,而不是按怎么做命名
最大障碍片段依赖的局部变量过多,可先拆分临时变量或改用查询替代临时变量
配套手法Move Function 搬家、Inline Function 反向合并

操作步骤

  • 先给新函数起名:用动词短语说明意图,而不是描述实现细节。
  • 把片段复制到新函数体里,把用到的局部变量变成参数或返回值。
  • 片段修改了多个局部变量时,优先想办法只返回一个值,减少接口面。
  • 编译并运行测试,确认新函数行为与原来完全一致。
  • 在原位置替换为对新函数的调用,再次运行测试。
  • 若新函数明显属于另一个类的职责,用搬家手法把它移过去。

提炼前后的差别

维度提炼前提炼后
可读性靠注释说明意图函数名本身就是意图
复用片段无法被其他位置调用可直接调用
测试只能整体测试外层函数可单独测试该片段
改动风险改动牵连整个长函数改动范围局部化

Java 示例

Java 示例
// 提炼前
void printOwing(double amount) {
    printBanner();
    System.out.println("name: " + name);
    System.out.println("amount: " + amount);
}

// 提炼后
void printOwing(double amount) {
    printBanner();
    printDetails(amount);
}

void printDetails(double amount) {
    System.out.println("name: " + name);
    System.out.println("amount: " + amount);
}
提炼时若临时变量过多:先拆分临时变量或改用查询替代临时变量,减少片段对局部状态的依赖,提炼会顺很多。
一次只提炼一个片段并立刻跑测试:连续提炼多处再统一验证,出问题时很难判断是哪一步引入的。

易错点

  • 提炼后的名称与行为不符:把注释换成名字却没换语义,比不加注释更误导。
  • 一次提取多层嵌套的代码块:中间变量传递复杂,应先拆小再提炼。
  • 为提炼而提炼:只调用一次且语义完整的三行代码,硬拆反而增加跳转成本。
  • 参数列表一路膨胀:把片段依赖的变量全塞成参数,等于把长函数换个地方。

数据来源与参考

  • 《重构》第 2 版 · 第 6 章(Extract Function)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌