第4章:重构目标设定——定义清晰的接口边界、模块划分、性能指标与可测试性目标
重构最怕什么?最怕你改了一堆代码,最后说不清楚到底改好了没有。
我见过太多团队,重构前热血沸腾,重构后一地鸡毛。为什么?因为目标没定清楚。你连「什么是好」都没定义,怎么知道做完了?
这一章,我们就来聊聊怎么把重构目标从「感觉」变成「数字」。
4.1 接口边界:先画线,再动手
接口边界,说白了就是「谁该管什么,谁不该管什么」。我个人的习惯是:先画图,再写代码。画图不是画 UML 那种大而全的,而是画模块之间的通信线。
核心原则:接口是契约,不是实现。接口只告诉别人「我能做什么」,不告诉别人「我怎么做的」。
举个例子,一个温控模块的接口应该长这样:
/* 好的接口:只暴露行为,不暴露内部 */
typedef struct {
float (*get_temperature)(void);
int32_t (*set_target)(float temp);
bool (*is_stable)(void);
} temp_controller_if_t;
而不是这样:
/* 坏的接口:把内部实现细节全暴露了 */
typedef struct {
uint32_t adc_value; /* 内部ADC值 */
float pid_kp; /* PID参数 */
uint8_t heater_pwm; /* 加热器PWM */
void (*calibrate)(void); /* 校准函数,调用方根本不需要知道 */
} temp_controller_t;
我在项目中遇到过一件事:一个同事把 SPI 的寄存器地址直接写在接口头文件里,结果换了芯片型号,所有调用方都得改。这就是接口边界没划清楚。
避坑指南:我曾经在一个项目里,接口头文件包含了硬件寄存器定义。后来换了 MCU,改了一个月。从那以后,我定了一条铁律:接口头文件里,不准出现任何硬件相关的宏或类型定义。
4.2 模块划分:高内聚、低耦合,但别过度
模块划分这件事,很多人一上来就想着「我要拆成 20 个模块」。其实没必要。模块划分的核心就两句话:
- 一个模块只做一件事(高内聚)
- 模块之间只通过接口通信(低耦合)
我一般按这个步骤来划分:
- 先列功能清单:把系统所有功能写出来,别管模块,先列全。
- 找自然边界:哪些功能经常一起变?哪些功能依赖同一个硬件资源?
- 画依赖图:模块 A 调用模块 B,箭头别画反了。
- 检查循环依赖:如果有 A→B→C→A,说明边界没划对。
注意:模块不是越细越好。拆得太碎,接口数量爆炸,维护成本反而更高。我见过一个项目,一个传感器驱动拆了 5 个文件,最后没人看得懂调用关系。
下面这张图,是我常用的模块划分参考结构:
4.3 性能指标:用数字说话
重构之后,系统是变快了还是变慢了?你得有数据。我一般会定这几个指标:
| 指标类别 | 具体指标 | 测量方法 | 典型目标 |
|---|---|---|---|
| 响应时间 | 中断响应延迟、任务切换时间 | GPIO 示波器打点 | < 10 μs |
| 吞吐量 | 每秒处理消息数、数据采样率 | 计数 + 定时器 | ≥ 1000 msg/s |
| 内存占用 | RAM 峰值、Flash 占用 | 编译器 map 文件 | ≤ 总资源 70% |
| CPU 负载 | 空闲任务占比 | RTOS 自带统计 | ≥ 30% 空闲 |
个人经验:我习惯在重构前先跑一遍性能基线,把数据记下来。重构过程中每改完一个模块,就跑一遍对比。这样能及时发现「改坏了」的情况。
举个例子,我曾经重构一个 CAN 通信模块。重构前,发送一条消息平均耗时 2.3 ms。重构后,我测出来是 1.1 ms。数据摆在那,谁都没话说。
4.4 可测试性目标:让测试成为习惯
很多嵌入式工程师觉得测试是 QA 的事。其实不对。可测试性,是设计出来的。
我定可测试性目标时,会关注这几点:
- 接口可模拟:每个模块的接口,都能用桩函数(stub)替换。
- 状态可观测:模块内部的关键状态,能通过接口读出来。
- 依赖可注入:模块依赖的外部资源(如定时器、存储),能通过参数传入。
一个简单的可测试性检查清单:
- ✅ 每个模块的接口是否独立?
- ✅ 能否在 PC 上编译运行单元测试?
- ✅ 模块内部是否有全局变量?如果有,能否通过接口访问?
- ✅ 硬件依赖是否被抽象到了 HAL 层?
我见过一个项目,所有模块都直接调用 HAL 的 GPIO 写函数。想测一个业务逻辑,得先接上硬件。后来我把 HAL 接口改成了函数指针表,测试时传入模拟函数,问题就解决了。
注意:可测试性不是一蹴而就的。我建议在重构初期就定好目标,比如「每个模块至少有一个单元测试」。别想着一次到位,先跑起来再说。
4.5 目标设定清单:照着做就行
最后,我整理了一份目标设定清单。每次重构前,我都会过一遍:
| 维度 | 目标 | 验收标准 |
|---|---|---|
| 接口边界 | 接口不暴露实现细节 | 头文件中无硬件宏、无全局变量 |
| 模块划分 | 无循环依赖 | 依赖图检查通过 |
| 性能指标 | 响应时间 < 10 μs | 示波器测量 100 次取平均 |
| 可测试性 | 每个模块可独立测试 | 单元测试覆盖率 ≥ 80% |