Skip to content

Refactor ​

解决什么问题:让代码结构持续跟随业务变化,不让结构变成业务演化的阻力——避免"修一个 bug 改 50 个文件" / "加一个 feature 拖 3 周",转而把重构当作持续的活动而不是一次性的大工程。

它是什么 ​

重构(Refactor) 是在不改变外部行为的前提下,改变代码内部结构的工程活动——这是 Martin Fowler 在《Refactoring》(1999/2018)中给出的经典定义:

  • 不改变外部行为——重构不是加 feature,不是修 bug
  • 改变内部结构——让代码更清晰、更模块化、更易理解、更易扩展
  • 小步快跑——一次重构一个小的改动,立刻有测试覆盖

IDDD Vernon Ch 11-14 把重构扩展到领域模型层——Aggregate 重构、Entity 行为重新划分、Value Object 重新识别——这是 DDD 层级的重构。 "Continuous refactoring of the domain model is essential to maintaining a healthy codebase"——领域模型必须随业务持续演化。

SE@G Ch 22 "Large-Scale Changes" 给出了重构的工程模板: "Large-scale changes should be approached as a series of small, atomic, reviewable changes—not as a single big-bang commit"——大规模重构必须是小步的、原子化的、可 review 的。

控制论 Ch 18 §18.3 自适应律(Adaptive Control): 重构 = 控制律的结构性调整——参数调优是自适应(小变),重构是结构演化(大变)。两者都是 B7 演化的具体形态。

V2 视角(V2.7 B7 段 5):重构 = B7 演化的战术工具——业务变了,代码结构必须跟着变;不重构 = 结构变成业务演化的阻力。

关键属性 ​

属性含义反模式
不改变行为重构前后外部行为完全一致把"修 bug"当作重构
小步进行一次只改一个维度一次性大重构
测试覆盖重构前后测试都通过没有测试就重构 = 赌博
持续进行不是一次性活动"等有空再重构" = 永远不重构
可回滚每次重构可独立回滚一次性 commit 改 100 个文件
动机驱动有明确"为什么重构""代码不好看"不够,必须有具体痛点
与迭代分离重构和加 feature 分开 commit"顺便重构一下" = 风险叠加
DDD 维度包含领域模型的结构调整只重构代码不重构模型 = 治标

怎么用 ​

场景 1:识别"该重构了" ​

4 个标志性信号:

1. 修一个 bug 要改 > 5 个文件
2. 加一个 feature 要 > 3 天
3. 测试覆盖开始下降
4. 团队开始说"别碰这块代码"

更精确的判断:
- Cyclomatic complexity 上升
- 类/函数越来越大
- 模块边界越来越模糊
- 命名和实际行为越来越不一致

场景 2:重构的标准流程(Fowler 模板) ​

1. 准备测试(保证现有行为被测试覆盖)
   ↓
2. 识别坏味道(Long Method / God Class / Feature Envy 等)
   ↓
3. 选择重构手法(Extract Method / Move Method / Rename 等)
   ↓
4. 小步应用(一次只做一个手法)
   ↓
5. 运行测试(保证没坏)
   ↓
6. commit(独立 commit,message 说明动机)
   ↓
7. 重复(继续下一个坏味道)

原则:永远不要在重构时加 feature——两者必须分开 commit。

场景 3:DDD 层级的重构(IDDD Ch 11-14) ​

聚合重构:
- 聚合边界重新划分(业务变了,原聚合不再合适)
- 聚合根替换(原来 Entity 不应该是聚合根)
- 不变量重新识别(业务规则变了)

实体 / 值对象重构:
- 值对象升级为实体(业务识别到身份)
- 实体降级为值对象(业务认识到可替换)
- 行为重新分配(哪个行为属于哪个对象)

领域服务重构:
- 业务逻辑从 Entity 移到 Domain Service
- 业务逻辑从 Application Service 移到 Domain

Vernon 反复强调:领域模型必须随业务持续重构——业务变了,模型不变 = 模型失真。

场景 4:大规模重构(SE@G Ch 22 模板) ​

场景:跨 100 个服务的接口签名修改

错误做法:一次 commit 改完所有 100 个服务 → 100 个团队同时升级 → 协调灾难

正确做法(SE@G Ch 22):
1. 工具辅助:LSC 工具自动生成原子化变更
2. 渐进发布:每个服务独立 PR,独立 review
3. 灰度上线:新旧接口并存,逐步切换
4. 旧接口 deprecation:标记 deprecated → 监控使用 → 给迁移路径 → 删除

关键:大规模重构 = 多个小重构的并行——而不是一个 big-bang 重构。

场景 5:重构的时机选择 ​

最差时机:发版前一周(测试压力大)
差时机:项目交付前
好时机:每次需求开发"顺便"做(10-20% 时间投入)
最佳时机:每个迭代周期(Sprint)预留 10-20% 时间做专门重构

原则:重构 = 持续活动,不是项目——和迭代混在一起做 = 风险叠加;单独做但持续 = 健康。

常见误区 ​

  1. "重构 = 重写" ——错。重构不改变外部行为;重写可能改变行为。两者完全不同
  2. "重构不需要测试" ——错(致命)。没有测试覆盖就重构 = 赌博——重构的前提是测试
  3. "重构 = 加 feature" ——错。重构不改行为;加 feature 改行为。混在一起 = 风险叠加,难回滚
  4. "一次性大重构" ——错(SE@G Ch 22 反对)。大规模重构必须原子化、渐进式、可 review
  5. "重构是架构师的事" ——错。每个工程师都该重构自己负责的代码;架构师负责跨团队的大规模重构
  6. "代码能跑就别动" ——错。"能跑" ≠ "能演化"——业务在演化,代码结构不演化 = 代码变成业务演化的阻力

关键洞察 ​

重构 = 不改变行为前提下改变代码结构 + 持续进行。 重构不是一次性项目,是持续活动——和迭代同步进行,每个 Sprint 预留 10-20% 时间。 控制论 Ch 18 自适应律(参数变)→ 重构(结构变);IDDD Ch 11-14 领域模型重构;SE@G Ch 22 大规模重构模板——三者共同构成 B7 演化的战术工具集。 判断标准:代码变成业务演化的阻力时(修 bug 难、加 feature 慢、测试覆盖降)= 必须重构。

相关链接 ​

从名家方法论与工程化思路中蒸馏出自己的工程体系。