重构基本功:提取函数、内联函数、提炼变量、内联变量

说实话,很多C语言开发者写了几年代码,重构的概念还是停留在「把代码改好看点」这个层面。但真正的重构,是有章法的。今天我要讲的这四个基本功——提取函数、内联函数、提炼变量、内联变量,就是重构的「四则运算」。你掌握了它们,代码质量至少能上一个台阶。

我在项目中见过太多「面条代码」了。一个函数几百行,变量名全是 abtmp。这种代码,别说维护,连读都费劲。而重构,就是从这些小地方开始改起的。

提取函数:把大块逻辑拆成小单元

提取函数,说白了就是把一段独立的逻辑从当前函数里「拎」出来,变成一个新的函数。为什么要这么做?因为一个函数只做一件事,才是好函数。

举个例子,你写了一段代码,用来计算两个复数的乘积:

// 重构前
void process_complex_data(double a_re, double a_im, double b_re, double b_im) {
    // 计算复数乘积
    double result_re = a_re * b_re - a_im * b_im;
    double result_im = a_re * b_im + a_im * b_re;
    
    // 后面还有几十行其他逻辑...
    // ...
}

这段代码的问题在哪?计算复数乘积的逻辑和主流程混在一起。如果后面还要在其他地方计算复数乘积,你就得复制粘贴。嗯,复制粘贴是代码腐烂的开始。

重构后:

// 重构后
typedef struct {
    double re;
    double im;
} Complex;

Complex complex_mul(Complex a, Complex b) {
    Complex result;
    result.re = a.re * b.re - a.im * b.im;
    result.im = a.re * b.im + a.im * b.re;
    return result;
}

void process_complex_data(Complex a, Complex b) {
    Complex result = complex_mul(a, b);
    // 后面继续处理...
}

核心要点:提取函数时,函数名要能准确描述「它做了什么」。我个人的习惯是,如果我想不出一个好名字,说明这段逻辑可能还不够独立,需要再想想。

小技巧:当你发现一段代码需要写注释来解释「它在干什么」时,这就是提取函数的好时机。把注释变成函数名,代码自解释。

内联函数:有时候拆得太碎也不是好事

有提取就有内联。内联函数是提取函数的逆操作。当一个函数体非常短,而且只被调用一次时,把它内联回去反而让代码更清晰。

我曾经接手过一个项目,里面有个函数只有一行:

int is_positive(int x) {
    return x > 0;
}

这个函数在整个项目里只被用了一次。你想想看,为了一个 x > 0 的表达式,你得多跳转一次、多维护一个函数声明。何必呢?直接内联掉:

// 内联后
if (x > 0) {
    // ...
}

注意:内联函数不是盲目合并。如果这个函数有明确的业务含义,即使只有一行,也值得保留。比如 is_admin_user() 这种,语义清晰,留着更好。

提炼变量:给「魔法值」和复杂表达式起个名字

提炼变量,就是把一个复杂的表达式或者一个硬编码的值,提取成一个有名字的变量。这样做的好处是:代码可读性提升,而且后续修改只需要改一个地方。

看这个例子:

// 重构前
if (status & 0x04) {
    // 处理写权限
}

这个 0x04 是什么?如果你不熟悉这个协议,你根本看不懂。提炼变量后:

// 重构后
const uint8_t WRITE_PERMISSION_MASK = 0x04;

if (status & WRITE_PERMISSION_MASK) {
    // 处理写权限
}

你看,代码瞬间就「说话」了。我在项目中经常遇到这种魔法数字,每次看到都忍不住重构掉。

再来看一个复杂表达式的例子:

// 重构前
if ((a > 0 && b > 0) || (a < 0 && b < 0)) {
    // a和b同号
}

提炼变量后:

// 重构后
int is_same_sign = (a > 0 && b > 0) || (a < 0 && b < 0);
if (is_same_sign) {
    // a和b同号
}

避坑指南:我曾经提炼变量时,把变量名起得太长,比如 is_user_has_write_permission_to_the_document。结果代码被拉得很长,反而不好看了。变量名要准确,但也要简洁。

内联变量:去掉多余的中间人

内联变量是提炼变量的逆操作。当一个变量只被使用一次,而且变量名并没有比表达式本身更清晰时,就应该内联掉。

举个例子:

// 重构前
int temp = x + y;
return temp;

这个 temp 变量完全多余。直接返回表达式:

// 重构后
return x + y;

再比如:

// 重构前
int result = compute_value(a, b);
return result;

如果 compute_value 这个名字已经足够清晰,那 result 这个变量就是多余的。直接 return compute_value(a, b); 就好。

我的经验:内联变量最常见的场景是在调试阶段。你为了看某个中间值,特意提取了一个变量。调试完了,这个变量就该删掉。别留着,留着就是技术债务。

知识体系总览

这四个基本功,其实构成了一个完整的「重构循环」。我画了一张图,帮你理清它们之间的关系:

重构基本功:四个核心操作 代码重构 提取函数 大块逻辑 → 独立函数 内联函数 单次调用 → 合并回去 提炼变量 魔法值/表达式 → 命名 内联变量 多余中间变量 → 直接使用 提取 ↔ 内联 互为逆操作 函数 ↔ 变量 两个维度 核心原则:每次只改一个点,改完就测试

总结一下

这四个操作,说白了就是「拆」和「合」的艺术。提取是拆,内联是合。提炼是命名,内联是简化。你不需要一次性把所有代码都重构完。我的建议是:每次修改代码时,顺手做一个小重构。比如加一个新功能前,先把周围的烂代码清理一下。

记住一个原则:让代码读起来像在讲故事。函数名是章节标题,变量名是角色名字。好的代码,不需要注释也能看懂。

最后说一句:重构不是炫技,而是为了让下一个接手你代码的人(很可能就是三个月后的你自己)少掉几根头发。这四个基本功,值得你花时间练熟。

公众号:蓝海资料掘金营,微信deep3321