代码审查与重构:代码规范、常见坏味道、重构手法
说实话,代码审查和重构这件事,很多工程师觉得是「锦上添花」。项目工期紧,能跑就行,谁还管代码好不好看?
但我做了十几年嵌入式开发,吃过太多亏了。有一次我接手一个老项目,里面有个函数足足800行,全局变量满天飞。我改一个bug,修好了这里,炸了那里。那段时间我做梦都在追变量。嗯,从那以后,我再也不敢轻视代码质量了。
今天我们就聊聊,怎么通过代码审查发现坏味道,再用重构手法把代码救回来。
一、代码规范:不是形式主义,是保命符
代码规范这东西,很多人觉得是「管得太宽」。但你想啊,一个团队五六个人,每个人写代码风格都不一样,那维护起来得多痛苦?
我个人习惯,规范分三层:
| 层级 | 内容 | 举例 |
|---|---|---|
| 命名规范 | 变量、函数、宏的命名规则 | 全局变量加 g_ 前缀,宏全大写 |
| 格式规范 | 缩进、括号、空行 | 函数之间空两行,缩进4空格 |
| 注释规范 | 什么该注释,什么不该注释 | 函数头说明功能、参数、返回值 |
二、常见坏味道:一眼就能看出问题
坏味道这个词,我第一次听觉得挺玄乎。后来发现,其实就是「代码看起来不对劲」。我总结了嵌入式C语言里最常见的几种:
1. 函数太长
一个函数超过50行,基本就该拆了。超过100行,那绝对是灾难。我在项目中见过一个函数,里面既有I2C通信,又有数据处理,还有状态机切换。说白了,它什么都干,什么都干不好。
2. 全局变量泛滥
全局变量用起来方便,但维护起来要命。你想想,一个变量在10个文件里被修改,出bug了你都不知道是谁改的。
3. 重复代码
Ctrl+C、Ctrl+V 一时爽,维护火葬场。同样的逻辑出现在三四个地方,改一个漏一个,bug就这么来的。
4. 魔法数字
代码里直接写 0x55、100、2000,没人知道什么意思。我建议用宏定义或者枚举,给数字起个名字。
// 坏味道
if (status == 0x55) {
delay_ms(100);
}
// 重构后
#define DEVICE_READY_STATUS 0x55
#define RETRY_DELAY_MS 100
if (status == DEVICE_READY_STATUS) {
delay_ms(RETRY_DELAY_MS);
}
三、重构手法:小步快跑,别搞大跃进
重构最忌讳什么?一上来就大改。我见过有人重构一个模块,改了三天,结果编译都过不了。最后回退,白干。
我的原则是:每次只改一点点,改完就测试。具体手法有这些:
1. 提取函数(Extract Function)
把一段逻辑独立出来,变成一个新函数。好处是:代码变短了,逻辑更清晰,还能复用。
// 重构前
void process_data(void) {
// 50行代码
// ... 校验和计算
uint8_t checksum = 0;
for (int i = 0; i < len; i++) {
checksum += buffer[i];
}
// ... 其他逻辑
}
// 重构后
static uint8_t calculate_checksum(uint8_t *buffer, int len) {
uint8_t checksum = 0;
for (int i = 0; i < len; i++) {
checksum += buffer[i];
}
return checksum;
}
void process_data(void) {
// 主逻辑变短了
uint8_t checksum = calculate_checksum(buffer, len);
// ... 其他逻辑
}
2. 引入变量(Introduce Variable)
复杂的表达式,拆成几个有意义的变量。可读性瞬间提升。
// 重构前
if ((status & 0x03) == 0x01 && timeout > 5000) {
// ...
}
// 重构后
bool is_device_ready = ((status & 0x03) == 0x01);
bool is_timeout = (timeout > 5000);
if (is_device_ready && is_timeout) {
// ...
}
3. 拆分条件(Decompose Conditional)
复杂的 if-else,拆成多个小函数。每个条件判断都一目了然。
// 重构前
if (temperature > 85 && humidity > 60 && fan_speed == 0) {
// 过热保护
}
// 重构后
static bool is_overheat_condition(int temp, int hum, int fan) {
return (temp > 85) && (hum > 60) && (fan == 0);
}
if (is_overheat_condition(temperature, humidity, fan_speed)) {
// 过热保护
}
四、代码审查流程:别走过场
很多团队的代码审查就是走个形式。代码发出来,没人看,或者随便扫一眼就过了。这不行。
我建议的流程是这样的:
- 自审:提交前自己先看一遍。我经常发现,自己写的代码过两天再看,就能看出问题。
- 小范围审查:找1-2个同事,别找太多。人多了反而没人认真看。
- 关注逻辑,不纠结格式:格式问题交给工具。审查的重点是:逻辑对不对?有没有边界情况?有没有资源泄漏?
- 给出具体建议:别说「这段代码不好」,要说「这里建议用状态机代替 if-else,更清晰」。
五、知识体系总览
下面这张图,是我自己总结的代码审查与重构的知识结构。你可以把它当成一个检查清单:
你看,整个知识体系其实不复杂。核心就是:规范打底,坏味道做预警,重构手法当工具,审查流程做保障。
公众号:蓝海资料掘金营,微信deep3321