# 代码质量与重构 ::: 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 指南是业界标杆,值得学习。