# 代码质量与重构
::: tip 前言
**代码写出来能跑就行了吗?** 你可能写过这样的代码:功能是实现了,但过了两周自己都看不懂了。或者团队里有人离职,留下一堆"只有上帝和他才能看懂"的代码。
本章带你理解什么是好代码,如何识别坏代码,以及如何安全地改进它。
:::
**这篇文章会带你学什么?**
| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 代码坏味道 | 识别常见问题 |
| **第 2 章** | 重构手法 | 安全地改进代码 |
| **第 3 章** | 代码审查 | 团队协作中的质量保障 |
| **第 4 章** | 质量度量 | 用数据衡量代码健康度 |
学完本章,你将掌握识别代码问题、安全重构、以及通过团队协作持续提升代码质量的方法。
---
## 0. 全景图:代码的生命周期
在软件开发中,有一个常被忽视的事实:**代码被阅读的次数远远多于被编写的次数**。
一段代码从诞生到退役,大致会经历这样的旅程:
::: tip 代码的一生
- **编写阶段**:开发者写下第一版实现,功能跑通了,测试通过了。
- **审查阶段**:团队成员阅读代码,提出改进建议。
- **维护阶段**:修 Bug、加功能、适配新需求——这个阶段占据了代码生命周期的 80% 以上。
- **重构阶段**:当代码变得难以维护时,需要在不改变外部行为的前提下改善内部结构。
- **退役阶段**:技术迭代,旧代码被新方案替代。
:::
Martin Fowler 在《重构》一书中说过:**"任何一个傻瓜都能写出计算机能理解的代码,唯有好的程序员才能写出人类能理解的代码。"**
---
## 1. 代码坏味道:识别常见问题
### 1.1 什么是代码坏味道?
"代码坏味道"(Code Smell)这个概念由 Kent Beck 提出,指的是代码中那些**虽然不是 Bug,但暗示着更深层设计问题**的特征。就像房间里有股怪味——不会立刻让你生病,但说明某个地方需要清理了。
通过下面的交互组件,识别几种最常见的代码坏味道:
### 1.2 常见坏味道清单
| 坏味道 | 症状 | 危害 |
|-------|------|------|
| **过长函数** | 函数超过 50 行 | 难以理解、测试和复用 |
| **魔法数字** | 代码中直接写 `86400000` | 含义不明,修改时容易遗漏 |
| **重复代码** | 相似逻辑出现在多处 | 修改时必须同步多处,容易遗漏 |
| **过深嵌套** | 超过 3 层的 if/for | 逻辑像迷宫,难以追踪 |
| **过长参数列表** | 函数参数超过 4 个 | 调用困难,容易传错顺序 |
| **上帝类** | 一个类/模块做了太多事 | 职责不清,牵一发动全身 |
::: tip 核心洞察
坏味道不是"错误",而是"信号"。它告诉你:这里的设计可能需要改进。不是所有坏味道都需要立刻修复,但你需要有能力识别它们。
:::
---
## 2. 重构手法:安全地改进代码
### 2.1 什么是重构?
重构(Refactoring)的定义非常精确:**在不改变代码外部行为的前提下,改善其内部结构。**
关键词是"不改变外部行为"。重构不是重写,不是加功能,不是修 Bug。它是对代码内部的"整理收纳"。
通过下面的组件,对比几种常见重构手法的前后变化:
### 2.2 常用重构手法
**提炼函数(Extract Function)**
这是最常用的重构手法。当一段代码可以用一个有意义的名字来概括时,就应该把它提炼成函数。
```javascript
// 重构前
function printReport(data) {
// 计算总价
let total = 0
for (const item of data.items) {
total += item.price * item.qty
}
// 打印...
}
// 重构后
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.qty, 0)
}
function printReport(data) {
const total = calculateTotal(data.items)
// 打印...
}
```
**重命名(Rename)**
好的命名是最廉价也最有效的文档。当你需要写注释来解释一个变量/函数的含义时,说明它的名字不够好。
```javascript
// 重构前
const d = new Date() - startTime // 经过的时间
const arr = users.filter(u => u.a) // 活跃用户
// 重构后
const elapsedMs = new Date() - startTime
const activeUsers = users.filter(user => user.isActive)
```
**用卫语句替代嵌套(Replace Nested Conditional with Guard Clauses)**
```javascript
// 重构前
function getPayAmount(employee) {
if (employee.isSeparated) {
return { amount: 0 }
} else {
if (employee.isRetired) {
return { amount: employee.pension }
} else {
return { amount: employee.salary }
}
}
}
// 重构后
function getPayAmount(employee) {
if (employee.isSeparated) return { amount: 0 }
if (employee.isRetired) return { amount: employee.pension }
return { amount: employee.salary }
}
```
::: tip 重构的安全网
重构最大的风险是"改着改着就改出 Bug 了"。所以重构的前提是**有测试覆盖**。每次小步重构后运行测试,确保行为没变。没有测试的代码,先补测试再重构。
:::
---
## 3. 代码审查:团队协作中的质量保障
### 3.1 为什么需要代码审查?
代码审查(Code Review)是团队中最有效的质量保障手段之一。它的价值不仅在于发现 Bug,更在于:
- **知识共享**:团队成员了解彼此的代码,降低"巴士因子"(如果某人被巴士撞了,项目还能继续吗?)
- **统一风格**:通过审查逐步形成团队的编码规范
- **提前发现设计问题**:比 Bug 更难修的是糟糕的架构决策
- **互相学习**:看别人的代码是提升编程能力的捷径
### 3.2 审查什么?
| 维度 | 关注点 |
|------|--------|
| **正确性** | 逻辑是否正确?边界条件是否处理? |
| **可读性** | 命名是否清晰?结构是否易懂? |
| **安全性** | 是否有注入风险?敏感数据是否暴露? |
| **性能** | 是否有明显的性能问题?N+1 查询? |
| **测试** | 是否有对应的测试?覆盖了关键路径吗? |
### 3.3 审查的礼仪
好的代码审查是**对代码的讨论,而不是对人的批评**:
- 用"我们"而不是"你":~~"你这里写错了"~~ → "这里我们可以考虑用 guard clause"
- 提问而不是命令:~~"改成 const"~~ → "这个变量后面会被重新赋值吗?如果不会,用 const 更安全"
- 给出理由:不只说"不好",要说"为什么不好"以及"怎样更好"
---
## 4. 代码质量度量
### 4.1 圈复杂度
圈复杂度(Cyclomatic Complexity)衡量代码中独立路径的数量。每个 `if`、`for`、`case`、`&&`、`||` 都会增加复杂度。
| 复杂度 | 评价 | 建议 |
|--------|------|------|
| 1-10 | 简单 | 容易理解和测试 |
| 11-20 | 中等 | 考虑拆分 |
| 21-50 | 复杂 | 必须重构 |
| 50+ | 不可维护 | 紧急重构 |
### 4.2 代码覆盖率
代码覆盖率衡量测试执行了多少比例的代码。常见指标:
- **行覆盖率**:被执行的代码行占总行数的比例
- **分支覆盖率**:被执行的条件分支占总分支的比例
::: tip 覆盖率的陷阱
80% 的覆盖率不代表代码质量好。覆盖率只能告诉你"哪些代码没被测试到",不能告诉你"测试是否有意义"。一个只断言 `expect(true).toBe(true)` 的测试可以提高覆盖率,但毫无价值。
:::
### 4.3 实用工具
| 工具 | 用途 |
|------|------|
| **ESLint** | JavaScript/TypeScript 静态分析 |
| **Prettier** | 代码格式化,统一风格 |
| **SonarQube** | 综合代码质量平台 |
| **Husky** | Git hooks,提交前自动检查 |
---
## 5. 总结
回顾这一路,我们从识别问题到解决问题,建立了一套完整的代码质量改进体系:
1. **识别**:学会闻到代码坏味道,知道哪里需要改进
2. **重构**:掌握安全的重构手法,在测试保护下小步改进
3. **协作**:通过代码审查,让团队共同守护代码质量
4. **度量**:用客观指标追踪代码健康度
::: tip 终极思考
代码质量不是一次性的工作,而是持续的习惯。就像保持房间整洁一样——不是等到乱得不行了才大扫除,而是每天随手整理。**童子军法则**说得好:离开时让代码比你来时更干净一点。
:::
---
## 延伸阅读
- **经典书籍**:Martin Fowler《重构:改善既有代码的设计》是这个领域的圣经。
- **代码整洁之道**:Robert C. Martin《Clean Code》提供了大量实用的编码原则。
- **实践工具**:尝试在项目中配置 ESLint + Prettier + Husky,体验自动化代码质量保障。
- **代码审查**:Google 的 Code Review 指南是业界标杆,值得学习。