🧩 第十一章 衍生:敏捷开发
📄 一句话概括

重构的验收标准只有一条:行为一点不变,结构变好——实测同一批 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. 检查有没有把【加功能】和【重构】混在同一个提交里。

常见误区

  1. 以为重构就是重写。重写会改行为,重构不改。
  2. 没有测试就开始重构。那是赌博。
自测三问(点开对答案) 1. 重构的定义是什么? —— 不改外部行为、只改内部结构;加功能不算重构。 2. 实测验证结果如何? —— 5 组输入两版输出完全一致;函数 1→4 个,最长函数 10→3 行。 3. 为什么重构前要有测试? —— 没有测试就不知道行为有没有变,等于赌博。

相关笔记