📄 本篇摘要
重构(refactoring)=在不改变程序外部行为的前提下,整理内部结构——让代码更易读、易改、易扩展。两个铁律:行为不变(加功能是加功能,整理是整理,不同时干)、有测试保平安(没测试的重构是蒙眼走钢丝)。
📝 关系
上一节:CI 与 CD | 下一节:测试驱动开发与 XP
一、为什么要重构:代码会腐化
代码每改一次就脏一点:临时补丁摞临时补丁、复制粘贴遍地开花。腐化代码的速度越来越快——越难读懂就越容易改错,越改错就越要打补丁,恶性循环。重构是定期还债:趁债务还小的时候还,别等破产。
二、坏味道(code smell)清单
Martin Fowler 的经典清单里最常见几种:
- 重复代码:两处相似=马上要变三处;
- 超长函数:一屏放不下=干了不止一件事;
- 深嵌套:5-1 讲过的嵌套税;
- 神秘命名:5-2 的
data2; - 发散/霰弹式修改:改一个需求要动 N 个文件(该合并)/一个类被 N 个需求反复戳(该拆分)。
三、常用手法与小步原则
常用手法:提取函数(长函数拆小)、改名(5-2 Demo 干过)、移动函数(放到它真正归属的类)、以查询取代临时变量。所有手法共享一条纪律:
每次只做一小步,做完立刻跑测试,绿了再走下一步。
小步的价值:一旦测试变红,嫌疑范围只有刚才那一步——定位成本趋近于零。大刀阔斧一次改完再测,红了你都不知道从哪查起。
四、动手 Demo:一次教科书式的小步重构
# 重构前:超长函数+深嵌套+重复(5-1 与本节的坏味道全家桶)
def print_invoice(orders):
total = 0
for o in orders:
if o["amount"] > 0:
if o["vip"]:
total += o["amount"] * 0.9
else:
total += o["amount"]
print("总金额:", total)
for o in orders:
if o["amount"] > 0 and o["vip"]:
print("VIP 折后:", o["amount"] * 0.9)
# 重构(提取函数 + 护卫句拍平 + 消重复)
def unit_price(order):
if order["amount"] <= 0:
return None
return order["amount"] * 0.9 if order["vip"] else order["amount"]
def calc_total(orders):
prices = [p for p in (unit_price(o) for o in orders) if p is not None]
return sum(prices)
def print_invoice_refactored(orders):
print("总金额:", calc_total(orders))
for o in orders:
p = unit_price(o)
if o.get("vip") and p is not None:
print("VIP 折后:", p)
orders = [
{"amount": 200, "vip": True},
{"amount": 80, "vip": False},
{"amount": 0, "vip": True},
]
print_invoice(orders)
print("--- 重构后 ---")
print_invoice_refactored(orders)
沙箱实跑输出:
总金额: 260.0
VIP 折后: 180.0
--- 重构后 ---
总金额: 260.0
VIP 折后: 180.0
看到什么算成功:重构前后输出逐行一致——这就是【行为不变】的验收。
读结果:重构后每个函数只干一件事(unit_price 算单价、calc_total 汇总、主函数只管打印),金额规则只有一份(原来打折逻辑写了两处)。下一步要改【VIP 九五折】时,改一处就行。
五、30 秒实验
把两版都改成【VIP 九五折】,故意只改重构前的其中一处打折逻辑——两版输出立刻分叉。重复代码=同一规则的两份拷贝,早晚会改得不一致。
六、常见误区
| 误区 | 真相 |
|---|---|
| 重构和加功能一起干 | 两条工作流分开;混在一起出了错分不清是改坏还是写错 |
| 没测试也能重构 | 行为不变拿什么证明?先补测试再动手(5-3 的规矩) |
| 重构是闲时大扫除 | 重构是日常小步还债;攒到【大扫除】的那天通常不会来 |
自测三问(点击展开)
1. **重构的定义里哪四个字是铁律?** 答:行为不变——外部可观察的行为一个字节都不能变。 2. **为什么小步走?** 答:每步后跑测试,红了嫌疑只有一步;大改再测定位成本爆炸。 3. **重复代码为什么是坏味道之首?** 答:同一规则多份拷贝,修改时改一漏三,不一致就是新 bug。- ☐ 5-11 学习完成
相关笔记
- 单元测试与集成测试 —— 重构的安全网
- 测试驱动开发与 XP —— 红-绿-重构的完整循环
- ⌨️ 编程原理 · 索引 —— 全课路线图