坏味道识别(上):重复代码、过长函数、过大的类、过长参数列表

各位同学,咱们今天聊聊代码里的「坏味道」。

你可能写过这样的代码:明明逻辑一样,却复制粘贴了七八遍;一个函数几百行,看得人头皮发麻;一个结构体塞了几十个字段,传参传得手抽筋。嗯,这些就是典型的坏味道。

我刚开始带项目时,也踩过这些坑。有一次接手一个老项目,光一个函数就两千多行,我盯着屏幕看了半小时,愣是没看懂它在干嘛。后来我花了整整一周重构,才把那个函数拆成二十多个小函数。从那以后,我对坏味道特别敏感。

核心观点:坏味道不是 bug,但它会慢慢把代码变成一坨「屎山」。识别坏味道,是重构的第一步。

一、重复代码(Duplicated Code)

重复代码,说白了就是「同样的逻辑写了好几遍」。这是最常见的坏味道,也是我最先想消灭的。

为什么会出现?

  • 赶工期,复制粘贴最快
  • 不同人写了类似功能,没人合并
  • 需求小改动,懒得抽象

怎么识别?

你看到两段代码,结构几乎一样,只是变量名不同。或者,你修改一个功能,发现要改三个地方。这就是重复代码。

// 坏味道:重复代码
void processA() {
    int i;
    for (i = 0; i < 10; i++) {
        printf("A: %d\n", i);
    }
}

void processB() {
    int i;
    for (i = 0; i < 10; i++) {
        printf("B: %d\n", i);
    }
}

你看,两个函数除了打印的字符不同,其他一模一样。这就是典型的重复。

我的习惯:写代码时,如果发现自己在复制粘贴,立刻停下来。先想想能不能抽成一个函数。哪怕多花五分钟,后面能省一小时。

怎么改?

提取公共部分成一个函数,用参数控制差异。

// 重构后
void process(char ch) {
    int i;
    for (i = 0; i < 10; i++) {
        printf("%c: %d\n", ch, i);
    }
}

你想想看,这样改完,以后要改循环逻辑,只需要改一个地方。是不是清爽多了?

二、过长函数(Long Function)

过长函数,就是函数太长,一眼看不到头。我个人觉得,一个函数超过 30 行就该警惕了。超过 50 行,基本可以断定需要拆。

为什么不好?

  • 难以理解:读一个长函数,你得记住前面做了什么,后面又做了什么
  • 难以测试:长函数通常做了好几件事,测试用例很难覆盖全
  • 难以复用:你想用其中一段逻辑,只能复制粘贴

我在项目中遇到过:一个函数处理网络请求,里面包含了数据解析、业务逻辑、日志记录、错误处理。整整 300 行。后来出 bug,定位问题花了三天。拆成 10 个小函数后,半小时就找到了问题。

// 坏味道:过长函数
void handleRequest(int sock) {
    char buf[1024];
    recv(sock, buf, sizeof(buf), 0);
    // 解析请求... 50行
    // 验证权限... 30行
    // 处理业务... 100行
    // 记录日志... 20行
    // 返回响应... 40行
}

怎么改?

原则:一个函数只做一件事。把每个「步骤」拆成独立函数。

// 重构后
void handleRequest(int sock) {
    char buf[1024];
    recv(sock, buf, sizeof(buf), 0);
    
    Request req = parseRequest(buf);
    if (!checkPermission(req)) {
        sendError(sock, "permission denied");
        return;
    }
    processBusiness(req);
    logRequest(req);
    sendResponse(sock, req);
}

注意:拆函数不是无脑拆。拆出来的函数要有明确的职责,名字要能表达「它做了什么」。我曾经见过有人把 300 行函数拆成 30 个 10 行函数,结果更难看懂。拆得太碎也是坏味道。

三、过大的类(Large Class)

C 语言没有 class,但结构体(struct)就是我们的「类」。过大的类,指的是一个结构体塞了太多字段,或者一个模块(.c 文件)包含了太多功能。

怎么识别?

  • 一个结构体有 20 个以上字段
  • 一个 .c 文件超过 1000 行
  • 一个模块的职责描述要用「和」「以及」连接
// 坏味道:过大的结构体
typedef struct {
    char name[64];
    int age;
    char address[256];
    char phone[20];
    float salary;
    int departmentId;
    char departmentName[64];
    int managerId;
    char managerName[64];
    // ... 还有 20 个字段
} Employee;

你看,这个结构体把员工个人信息、部门信息、上级信息全塞一起了。说白了,它至少可以拆成三个结构体。

怎么改?

按职责拆分。每个结构体只负责一个「概念」。

// 重构后
typedef struct {
    char name[64];
    int age;
    char address[256];
    char phone[20];
} PersonalInfo;

typedef struct {
    int id;
    char name[64];
} Department;

typedef struct {
    int id;
    char name[64];
} Manager;

typedef struct {
    PersonalInfo personal;
    Department dept;
    Manager mgr;
    float salary;
} Employee;

避坑指南:我曾经把一个结构体拆得太细,结果传参时传了五六个小结构体,反而更乱。拆分的粒度要适中,一般一个结构体 5-8 个字段比较合理。

四、过长参数列表(Long Parameter List)

函数参数太多,调用时写一长串,看着就头疼。我个人觉得,超过 3 个参数就该考虑优化了。超过 5 个,基本是坏味道。

为什么不好?

  • 调用者容易搞错顺序
  • 新增参数要改所有调用处
  • 函数职责不清晰
// 坏味道:过长参数列表
void createUser(char* name, int age, char* phone, char* email, 
                char* address, int deptId, float salary, int managerId) {
    // ...
}

// 调用时
createUser("张三", 28, "13800138000", "zhangsan@example.com", 
           "北京市海淀区", 101, 15000.0, 201);

你想想看,调用这个函数的人,得记住每个参数的位置。万一传错了,bug 很难查。

怎么改?

把相关参数封装成结构体。

// 重构后
typedef struct {
    char* name;
    int age;
    char* phone;
    char* email;
    char* address;
} ContactInfo;

typedef struct {
    int deptId;
    float salary;
    int managerId;
} WorkInfo;

void createUser(ContactInfo* contact, WorkInfo* work) {
    // ...
}

// 调用时
ContactInfo contact = {"张三", 28, "13800138000", "zhangsan@example.com", "北京市海淀区"};
WorkInfo work = {101, 15000.0, 201};
createUser(&contact, &work);

核心原则:参数列表应该反映函数的「核心依赖」。那些经常一起变化的参数,就应该封装在一起。

知识体系总览

下面这张图,帮你理清这四种坏味道的关系和解决思路。

坏味道识别(上)知识体系 四种坏味道 重复代码 过长函数 过大的类 过长参数列表 解决方案:提取函数 / 封装结构体

小结

今天聊了四种坏味道:重复代码、过长函数、过大的类、过长参数列表。它们就像代码里的「慢性病」,不会立刻让你崩溃,但会慢慢拖垮你的项目。

识别它们其实不难:

  • 看到复制粘贴,就是重复代码
  • 函数超过 30 行,警惕过长函数
  • 结构体字段超过 10 个,考虑拆分
  • 参数超过 3 个,封装成结构体

记住一句话:好的代码不是写出来的,是改出来的。重构不是一次性的工作,而是日常的习惯。下次写代码时,多问自己一句:「这段代码有没有坏味道?」

我的建议:从今天开始,每次提交代码前,花 5 分钟检查一下有没有这四种坏味道。坚持一个月,你会发现自己写代码的质量明显提升。

可可精品资料库