返回 AI Coding 培训
实操 11/11

用镀金玫瑰练习重构:先用测试守住行为,再消灭坏味道

5 分钟
AI编程效率方法职业发展

课程收尾时,我没有再让同学们从零生成应用,而是给出经典的 Gilded Rose 遗留代码练习。代码能运行,却充满嵌套判断、重复分支和令人费解的命名。AI 很容易给出一句“我已经重构完成”,但真正困难的是:我们如何证明它没有改变原有行为?

本文采用社区常见译名“镀金玫瑰”。练习的重点不是得到一份公认最漂亮的代码,而是建立一条可审查、可回退、行为受保护的重构路径。

前置条件

  • 从 Gilded Rose Refactoring Kata 仓库选择熟悉的语言版本。
  • 确认项目原始测试命令能够运行。
  • 不在第一步修改生产代码。
  • 为练习建立独立 Git 分支,并保存初始基线。

第一步:先理解外部行为,不急着评价写法

先读需求说明、入口和已有测试,列出商品名称、销售剩余天数和品质变化之间的规则。此时可以让 AI 用 Ask 只读分析,但要求每条结论对应代码或测试证据。

遗留代码里的命名和结构可能很差,当前行为也未必完全符合你的直觉。重构的第一目标是保持已经存在且需要保留的外部行为;如果要修业务缺陷,应作为另一项需求单独处理。

第二步:建立可执行的行为基线

先运行现有测试并记录结果。若覆盖不足,为代表性规则补充特征测试:普通商品、Aged Brie、Backstage passes、Sulfuras,以及销售日期前后和品质上下界。

可以使用参数化表格表达输入和预期,但每一行都应独立可读。不要把几十个断言藏进依赖共享状态的循环,也不要把当前实现逐行复制到测试里。测试描述的是可观察行为,而不是替旧代码写注释。

如果某条规则无法从需求或现有行为判断,先记录未知并提问,不让 AI 自行补成“最佳实践”。

第三步:识别坏味道,选择一个最小切口

常见问题包括:

  • 多层嵌套条件掩盖了业务规则。
  • 同一种边界判断在多个分支重复。
  • 技术性名称没有表达商品策略。
  • 一个函数同时负责分类、计算和更新状态。
  • 新增一种商品必须修改大量旧分支。

不要一次消灭所有问题。第一步可以只是提取一个有业务含义的小函数、消除一段重复条件或用提前返回降低嵌套。改动后立即运行测试并提交。

第四步:用小步循环约束 AI

给 Agent 的任务可以写成:

当前测试全部通过。只提取普通商品品质更新逻辑,不改变公共接口和任何输出;完成后运行测试,列出实际修改文件和仍存在的坏味道。

每一轮遵循:选择一个坏味道、做一个结构变化、运行测试、查看差异、提交 Git。这样即使某轮方向错误,也只需要回退一个小提交,而不是在大规模改写里寻找行为变化。

第五步:结构方案没有唯一答案

当不同商品拥有稳定且独立的更新策略时,策略对象、子类或工厂可能让新增规则更清楚;也可以用数据驱动表、映射函数或更小的纯函数完成。继承和工厂只是候选方案之一,不是这道题的唯一标准答案。

选择方案时问四个问题:

  1. 新增规则要修改多少旧代码?
  2. 每条业务规则能否在一个位置读懂?
  3. 测试失败时能否快速定位?
  4. 当前项目规模是否值得引入额外抽象?

过早建立复杂类层级,同样可能把简单规则藏进更多文件。重构目标是可理解和可变化,而不是使用最多设计模式。

第六步:用证据确认只改变结构

完成后至少检查:

  • 所有原有与新增特征测试通过。
  • 公共接口、输出和边界行为未改变。
  • Git 差异中没有顺手加入新需求。
  • 函数与变量名称更接近业务语言。
  • 新增一种商品时,修改范围比重构前更可控。

测试绿色不是全部。还需要人工评审抽象是否真的降低理解成本,是否把重复转移到了别处,以及测试是否只是在迎合新实现。

成功标准

  • 重构前已有可执行、可说明的行为基线。
  • 每次只做一类结构改动,并立即运行相关测试。
  • 任何时刻都能回退到最近可用提交。
  • 重构没有混入功能变化或需求修复。
  • 最终代码更接近业务语言,复杂度有清楚下降。
  • 能解释所选结构的权衡,而不是只说“AI 推荐”。

常见报错与处理

AI 直接重写整个函数

停止并缩小任务。保留测试,要求只做一次提取、改名或去重;审查差异后再进入下一轮。

测试全部通过但不敢确认行为

检查覆盖是否只包含正常路径。补销售日期边界、品质上下限和各类特殊商品的特征测试,再审查是否存在未记录规则。

为每种商品建立很多空类

回到变化原因和项目规模。如果类层级没有减少条件判断或提高新增规则的局部性,纯函数或映射可能更直接。

重构时顺手修了业务问题

把行为修改拆到独立分支或提交,先确认需求和新验收,再实施修复。不要让结构变化和业务变化共享同一次评审。

课后练习

选择 Gilded Rose 中最难读的一段,建立行为测试后完成三次不改变行为的小重构,每次独立提交。最后比较第一次和最后一次代码,写下一个被消除的坏味道、一个仍然保留的问题,以及为什么没有继续抽象。

参考资料

AI编程效率方法职业发展