📄 一句话概括
重构的验收标准只有一条:行为一点不变,结构变好——实测同一批 5 组输入两版输出完全一致,而函数从 1 个变 4 个、最长函数从 10 行变 3 行。
📝 本节和前面几节的关系
11.10 讲了 TDD,这一节讲循环的第三步:重构。TDD 造安全带,重构才敢开快车。
一、重构的定义
不改外部行为,只改内部结构。 这是唯一的定义。 所以【顺手加个功能】不是重构,那是改需求,必须分开做。 分开的好处:结构问题的改动不会和功能改动混在一个提交里。
二、实测:行为不变,结构变好
重构前:1 个函数、最长 10 行,求和加税保底取全塞在一起。
重构后:4 个小函数(positive/double/cap/after),最长只有 3 行。
5 组输入跑两版,输出完全一致——这就是重构的验收标准。
三、为什么必须有测试
没有测试的重构等于赌博——你怎么知道行为没变? 所以重构前先补测试,哪怕只补覆盖主路径的几条。 这也是 TDD 和重构连着讲的原因:前者造安全带,后者才敢动刀。
四、重构的频率
小步快跑:一次只改一个点,改完跑测试。 随时可以做——不必等【有空的时候】,因为通常等不到那一天。 难改的地方就是该重构的地方——改不动恰恰说明结构有债。
五、动手 Demo:在程序完成后改善运行代码的重构
重构前是一个函数包四件事,重构后拆成四个小函数。 用同一批 5 组输入跑两版,检查输出是否一致。
def before(xs):
total = 0
for x in xs:
if x > 0:
total += x
total = total * 2
if total >= 1000:
total = 1000
return total
def positive(xs): return [x for x in xs if x > 0]
def double(s): return s * 2
def cap(n): return n if n < 1000 else 1000
def after(xs): return cap(double(sum(positive(xs))))
data = [(1, -2, 3), (), (500, 600), (-1, -1), (7,)]
print(len(data), all(before(d) == after(d) for d in data)) # 5 True
实跑输出:
--------------------------------------------------------------------
在程序完成后改善运行代码的重构
--------------------------------------------------------------------
重构的判据是:**行为一点不变,结构变好。**实测同一批输入跑两版:
重构前的结构:1 个函数,最长 10 行
重构后的结构:4 个函数,最长 3 行
测试输入的组数 5 组
两组输出完全一致 True
函数个数 1 → 4
最长函数行数 10 → 3
行为是否改变 False(这就是重构的前提)
**【输出完全一致】是重构的验收标准**——不一致就不叫重构,叫改需求。
**所以重构之前必须有测试**:没有测试的重构,等于赌博。
**这也是 11.10 和 11.11 为什么连着讲**:TDD 造出安全带,重构才敢开快车。
实测:5 组输入两版输出完全一致,而函数从 1 个变 4 个、最长函数 10 行变 3 行。 输出一致是重构的验收标准——不一致就叫改需求,不叫重构。 没有测试的重构等于赌博,所以 TDD 和重构是连着讲的。
30 秒实验
- 挑一个长函数,先补三条测试,再拆成三个小函数。
- 检查有没有把【加功能】和【重构】混在同一个提交里。
常见误区
- 以为重构就是重写。重写会改行为,重构不改。
- 没有测试就开始重构。那是赌博。
自测三问(点开对答案)
1. 重构的定义是什么? —— 不改外部行为、只改内部结构;加功能不算重构。 2. 实测验证结果如何? —— 5 组输入两版输出完全一致;函数 1→4 个,最长函数 10→3 行。 3. 为什么重构前要有测试? —— 没有测试就不知道行为有没有变,等于赌博。- ☐ 11.12 经常进行系统整合的持续集成
相关笔记
- 先编写测试代码,一边运行一边开发的测试驱动开发 — 安全带来源
- 设计目标之一:去除重复 — 重构的常见目标
- 经常进行系统整合的持续集成 — 让重构安全的那道网
- 🧩 面向对象是怎样工作的 · 索引