代码简化
简化代码以提升清晰度。适用于在不改变行为的前提下重构代码以提高可读性。
通过降低复杂性来简化代码,同时保持完全一致的行为。目标是让代码更易于阅读、理解、修改和调试。
不要改变代码的功能——只改变表达方式。
每次修改前都要确认:
→ 对每个输入是否产生相同输出?
→ 是否保持相同的错误行为?
→ 是否保持相同的副作用和执行顺序?
→ 所有现有测试是否不需修改即可通过?
简化意味着让代码与代码库更加一致,而不是强加外部偏好。
// 不清晰:密集的三元表达式链
const label = isNew ? 'New' : isUpdated ? 'Updated' : isArchived ? 'Archived' : 'Active';
// 清晰:可读的映射
function getStatusLabel(item: Item): string {
if (item.isNew) return 'New';
if (item.isUpdated) return 'Updated';
if (item.isArchived) return 'Archived';
return 'Active';
}
警惕过度简化的陷阱:
默认只简化最近修改的代码。避免顺手重构不相关的代码。
在修改任何代码之前,先理解它存在的原因:
简化前先回答:
- 这段代码的职责是什么?
- 谁调用它?它调用谁?
- 边界情况和错误路径有哪些?
- 是否有定义预期行为的测试?
- 为什么它被写成这样?
结构复杂性:
| 模式 | 信号 | 简化方式 |
|------|------|----------|
| 深层嵌套(3+层) | 控制流难以跟随 | 提取为卫语句 |
| 长函数(50+行) | 多个职责 | 拆分为专注的函数 |
| 嵌套三元表达式 | 需要心理栈 | 替换为 if/else 链 |
| 布尔参数标志 | doThing(true, false, true) | 替换为选项对象 |
| 重复条件判断 | 相同的 if 检查出现在多处 | 提取为谓词函数 |
命名和可读性:
| 模式 | 信号 | 简化方式 |
|------|------|----------|
| 通用名称 | data、result、temp | 重命名以描述内容 |
| 误导性名称 | 名为 get 的函数实际在修改数据 | 重命名以反映行为 |
| 解释"做什么"的注释 | // increment counter | 删除注释 |
| 解释"为什么"的注释 | // Retry because API is flaky | 保留——它们传达意图 |
冗余:
| 模式 | 信号 | 简化方式 | |------|------|----------| | 重复逻辑 | 相同的 5+ 行代码出现在多处 | 提取为共享函数 | | 死代码 | 不可达分支、未使用变量 | 移除 | | 不必要的抽象 | 无价值的包装器 | 内联 | | 过度工程化的模式 | 工厂的工厂 | 替换为简单方案 |
一次进行一项简化。每次变更后运行测试。
对比修改前后:
- 简化后的版本是否确实更易理解?
- 是否引入了与代码库不一致的新模式?
- diff 是否干净且可审查?
- 团队成员是否会批准这个变更?
// 简化:不必要的 async 包装
// 修改前
async function getUser(id: string): Promise<User> {
return await userService.findById(id);
}
// 修改后
function getUser(id: string): Promise<User> {
return userService.findById(id);
}
// 简化:冗长的条件赋值
// 修改前
let displayName: string;
if (user.nickname) {
displayName = user.nickname;
} else {
displayName = user.fullName;
}
// 修改后
const displayName = user.nickname || user.fullName;
// 简化:手动构建数组
// 修改前
const activeUsers: User[] = [];
for (const user of users) {
if (user.isActive) {
activeUsers.push(user);
}
}
// 修改后
const activeUsers = users.filter((user) => user.isActive);
完成简化后: