重构手法目录(Martin Fowler《重构》第 2 版)

共 18 题
📑 题目列表 18 题
#
★★★

1. 重构的"业务影响"(business impact)的评估

在评判一次重构是否值得时,如何评估它的业务影响(business impact)?

  • 业务影响与重构收益的关联
  • 从业务价值角度评估重构优先级
  • 用业务指标衡量重构成效

重构的业务影响评估是指从"业务价值"而非单纯"代码美观"角度判断重构是否值得。核心是看重构能否用业务语言描述收益:如缩短新功能交付时间、降低缺陷率、加快响应变更、提升系统稳定性、减少维护人力等。评估时把重构与业务目标挂钩——优先重构那些"支撑核心业务流程、未来会被频繁改动"的模块,因为它们的结构改善直接转化为更快的业务迭代。同时用可观测的业务指标(如功能上线周期、缺陷数、线上事故)来验证重构是否真的带来改善。

重构常被认为"只花钱不挣钱",业务影响评估正是为了让重构可被业务方理解和支持。它把"技术债"翻译成"业务成本":结构差的代码拖慢交付、增加缺陷,都是有业务代价的。评估时既要看"当前痛点"(哪块代码正在拖慢什么),也要看"未来趋势"(哪块会高频演进),从而把重构预算投向业务价值最高的地方。

#
★★★

2. 重构的"停止条件"(stop condition)中何时停止重构、避免过度抽象

重构的停止条件(stop condition)是什么?如何避免过度抽象?

  • 停止条件的定义:达到目标即停,避免无谓延伸
  • 过度抽象的风险
  • 用"目标驱动"判断何时停止

重构的停止条件是指"当本次重构达到预期目标时即停止",而不是无限追求"完美结构"。每次重构前应明确目标(如降低某个方法的圈复杂度、消除某项重复、让某模块可测试),目标达成即停止。过度抽象是违背停止条件的典型反模式:为了"更优雅"不断引入接口、抽象基类、工厂、泛型,导致间接层激增、代码难以理解,反而损害可维护性。判断止损的标准是"新抽象是否带来可感知的清晰度与复用收益"——若没有,就停止。

重构是有边界、有目标的工程活动,而非审美洁癖的无限延伸。良好的停止条件与"过度抽象"的反模式相辅相成:一是看目标是否达成,二是看是否出现"为了抽象而抽象"的迹象。实践中常结合"小步重构"与测试验证,每步都问"这步是否真的有必要",用克制换来可维护性。

#
★★★

3. 重构的"对比基线"(baseline comparison)中重构前后的客观对比

什么是重构的对比基线(baseline comparison)?为什么重构前后要做客观对比?

  • 对比基线的定义:重构前记录的可度量状态
  • 通过对比验证重构效果与行为不变
  • 需要对比的维度(行为、质量、性能)

对比基线是指在重构前记录代码的可度量状态,作为重构后对比的参照。它的价值在于提供"客观验证":重构前先记录行为(测试快照)、性能(P99)、复杂度(圈复杂度)、覆盖率等指标,重构后再测同一批指标,用差异来证明"行为未变、结构更好"。没有基线就无法判断重构是否成功——既不能证明行为保持一致,也无法量化质量提升。

"行为不变"是重构的铁律,而对比基线正是验证这一铁律的手段。基线要选可重复、可量化的指标,并确保测量条件一致(同样的输入、环境、数据)。基线的对比既是行为安全的验证,也是"重构确实改善了什么"的客观证明,避免用主观感觉代替事实。

#
★★★

4. 重构的"成本边界"(cost boundary)中何时投入产出比失衡

什么是重构的成本边界(cost boundary)?如何判断投入产出比何时失衡?

  • 成本边界的定义:重构投入与收益的平衡点
  • 判断投入产出比失衡的信号
  • 用变更频率与收益估算做决策

成本边界指重构投入与收益的平衡点,超过这个点后重构的投入产出比失衡,不值得继续。判断时需要估算"重构的成本"(人力、风险、回归测试、上线时间)与"收益"(未来改动成本下降、缺陷减少、交付加快)。当收益难以覆盖成本时,例如代码虽差但稳定、未来改动极少、或重构风险极高,就应停止并记录为技术债。用变更频率作为重要参考:高频变更区域的收益高,成本边界宽松;低频区域的收益低,成本边界收窄。

成本边界与"停止条件"呼应,但更强调"收益-成本"的经济账。关键不是"能不能重构",而是"值不值得现在重构"。判断时把收益折算成"未来节省的改动成本",与当前投入的人力与风险对比。若收益现值低于成本,宁可容忍坏味道,把资源投向收益更高的地方。

#
★★★

5. 重构的"收益验证"(benefit verification)中度量改善的实际效果

如何度量并验证重构带来的实际改善效果(收益验证)?

  • 收益验证的定义:用可度量指标验证重构改善
  • 验证的维度:质量、性能、可维护性、交付
  • 长期跟踪与短期验证结合

收益验证指用可度量、可观测的指标,验证重构确实带来了实际改善,而非凭感觉。验证维度包括:可维护性(圈复杂度、耦合度、重复率下降)、质量(缺陷率下降、测试通过率与覆盖率提升)、性能(P99、吞吐量不退化或更优)、交付效率(新功能交付周期缩短、变更平均耗时下降)。验证方法是用重构前的基线对比重构后的数据,并结合长期跟踪(如每月看缺陷趋势、变更成本)确认改善是持续而非一次性。

收益验证让重构从"主观良好"变为"可证明的改善"。短期看行为不变与质量指标,长期看业务指标(缺陷、交付周期)。验证要选与重构目标一致的指标,避免用无关指标掩饰真实效果。这样既对重构负责,也为后续重构决策积累依据。

#
★★★

6. 重构的"过度抽象"(over-abstraction)的反模式诊断

如何诊断重构中的过度抽象(over-abstraction)反模式?它的表现与危害是什么?

  • 过度抽象的表现:接口、基类、工厂、泛型泛滥
  • 诊断信号下的危害
  • 化解手段:内联、简化、删除无关抽象

过度抽象是为追求"优雅"而引入过多抽象层,导致间接层激增、代码难以理解。诊断信号包括:一个接口只有一个实现、一个方法只是转发、抽象基类没有实质复用、工厂只为"将来可能"的扩展而建、泛型参数无实际泛化收益、为了"可扩展性"预先设计用不到的接口。危害是阅读成本高、调试困难、新手难以上手、修改需要在多个间接层间跳转,反而损害可维护性与开发效率。化解手段是使用 Inline Method、Inline Class 删除冗余抽象,把未体现复用的接口换回具体类,回归"够用即可"的设计。

过度抽象本质是"过度设计":为猜想的未来需求付出当前的成本。诊断的关键是"抽象是否带来真实收益"——每层抽象都应回答"它解决了什么具体问题、被谁复用",答不上来就应删除。与停止条件呼应,过度抽象是"重构时忘了目标"的典型症状,克制是修正之道。

#
★★★

7. 重构(Refactoring, Fowler)的"两顶帽子"(Two Hats)中新增功能 vs 重构的边界

Fowler 提出的"两顶帽子"(Two Hats)是什么?新增功能与重构如何区分边界?

  • 两顶帽子的定义:添加功能 vs 重构是两种不同活动
  • 切换帽子的纪律:一次只戴一顶帽子
  • 分区执行以避免混入行为变更

Fowler 的"两顶帽子"指开发时戴两顶帽子:一顶是"添加功能"(行为变化,测试从红到绿),一顶是"重构"(行为不变,只改变结构)。关键纪律是同一时刻只戴一顶帽子——要么添加功能不改变结构,要么重构不改变行为,两者不能混在同一操作里。切换帽子时,先完成当前帽子下的测试验证再切换,这样当行为变更失败时,能清晰定位是功能问题还是结构问题。

两顶帽子解决"重构与改功能混在一起"导致的验证困难。若边改结构边加功能,一旦出错无法判断是结构问题还是行为问题。严格执行两顶帽子,让每次提交要么是"行为变化"要么是"结构变化",配合测试在边界处把关,从而保证重构安全。这也是"小步提交"和"每个提交独立可回滚"的基础。

#
★★★

8. Replace Conditional with Polymorphism 的适用条件中基于类型分支的行为分发重构为多态

Replace Conditional with Polymorphism 的适用条件是什么?如何把基于类型分支的分发重构为多态?

  • 适用条件:分支基于类型编码或对象类型,且行为不同
  • 重构步骤:抽取子类、把分支移到子类方法
  • 与简单条件判断的取舍

Replace Conditional with Polymorphism 适用于"同一处基于类型编码或对象类型进行分支,且各分支行为不同"的场景——典型如 switch/if-else 依据 type 字段分发不同行为。适用条件包括:分支类型数量稳定或明确、各分支逻辑独立且会分别演进、类型编码在运行时确定。重构步骤是先把每种类型用子类表示,再把分支逻辑提取为各子类覆盖的公共方法,用多态调用替代条件判断。若分支只是简单赋值或逻辑极短、类型很少,用简单的条件或查找表反而更清晰,不必强行多态。

多态的目的是"让行为随类型自然分布",避免条件分支随类型增多而膨胀。判断是否应多态化的关键是"分支是否随类型扩展而反复修改"——若每次加类型都要改这段 switch,就应多态化;若类型固定、分支简单,保持条件更简单。重构后新增类型只需新增子类,符合开闭原则。

#
★★★

9. Branch by Abstraction 在跨多团队同时重构时的协调

Branch by Abstraction 是什么?它在跨多团队同时重构时如何协调?

  • Branch by Abstraction 的定义与步骤
  • 在共享代码库上渐进重构的机制
  • 多团队协调与风险控制

Branch by Abstraction 是一种渐进式重构技术:先在要替换的模块前面引入一个抽象接口,让调用方改写为依赖该接口,然后并行实现新旧实现,最后逐步切换调用方到新实现,再删除旧实现。它在跨多团队同时重构时尤其有用,因为多个团队可以各自独立地、小步地把对旧实现的依赖改为对抽象接口的依赖,无需一次性大爆炸迁移;各团队进度不同也能在共享代码库上共存,因为抽象接口屏蔽了新旧实现的差异。

相比传统分支合并,Branch by Abstraction 在主干上渐进演进,避免长期分支的合并冲突与"大爆炸"风险。多团队协调的关键是"以抽象接口为契约":先统一接口定义,各团队按接口开发,通过契约测试保证实现兼容,再分批切换。它让重构跨越团队边界时仍可小步、可回滚、可并行。

#
★★

10. 重构的"小步重构"(small steps)中每个 commit 一个独立重构

什么是小步重构?为什么每个 commit 应只包含一个独立重构?

  • 小步重构的定义:一次一个小的、可验证的改动
  • 单 commit 单重构的价值:可回滚、易定位、易评审
  • 小步重构的粒度把握

小步重构指把一次大的重构拆成多个小的、可独立验证的改动,每步改动都较小且能通过测试。每个 commit 只包含一个独立重构,价值在于:问题可定位(出错时能确定是哪一步引入)、可回滚(坏 commit 可单独撤销而不影响其他)、可评审(reviewer 能理解每个改动意图)、可合并(减少冲突)。粒度上以"一个行为不变、可编译、可测试的小改动"为一个 commit。

小步重构是"大重构风险分散"的核心手段。它让行为不变变得可逐步验证,每步保证测试通过,把失败风险限制在单步内。配合"每个提交可独立回滚"的拆分标准,小步重构把重构从"一次赌博"变成"一系列可验证的小步骤"。

#
★★

11. 重构的"测试先行"(test-first)中 characterization test 覆盖遗留代码

什么是测试先行(test-first)?characterization test(特征测试)如何覆盖遗留代码?

  • 测试先行的定义与价值
  • characterization test 的定义:记录现有行为而非期望行为
  • 在遗留代码重构中的应用

测试先行指在重构前先编写测试,为重构提供安全网。characterization test(特征测试)是专门为"行为未明、无测试"的遗留代码编写的测试:它不测试"期望的正确行为",而是"记录并固定当前实际行为",把现状固化为可回归的基线。这样重构前后都跑同一批特征测试,一旦行为改变,测试失败即提示重构改变了行为。它让没有测试的遗留代码也能安全重构。

特征测试的价值在于"把未知行为变成可验证的契约"。对遗留代码先写测试往往困难,因为不知道正确行为是什么;特征测试绕开这一问题,直接记录现状。重构后若特征测试仍通过,说明行为未变;若失败,则需判断是重构引入的回归还是测试定义本身。它是"重构安全网"在遗留代码场景的关键落地。

#
★★

12. 重构的"红绿重构循环"(red-green-refactor cycle)中 TDD 的第三步

什么是红绿重构循环(red-green-refactor cycle)?为什么它是 TDD 的第三步?

  • 红绿重构循环的三个阶段:红(写失败测试)、绿(让它通过)、重构(改进结构)
  • 重构作为 TDD 第三步的定位
  • 循环中行为保持不变

红绿重构循环是 TDD 的核心循环:第一步"红"是写一个失败的测试(先写测试再写实现);第二步"绿"是写最简实现让测试通过;第三步"重构"是在测试全绿的前提下,改进代码结构而不改变行为。重构是 TDD 的第三步,因为它依赖前两步产出的测试作为安全网——只有测试全绿,才能放心地整理结构、消除重复,并把"两顶帽子"的纪律贯彻到循环中。

TDD 强调"先测试后实现",而重构是循环中"测试已绿、行为已定"之后的结构优化阶段。红绿重构循环把"验证"与"改善"交替进行:红绿保证行为正确,重构保证结构良好。这样既边写边测,又时刻保持结构清晰,避免只顾功能不管质量。

#
★★

13. 重构的"代码量变化"(LOC delta)中净增减的客观统计

什么是重构的代码量变化(LOC delta)?为什么净增减是客观统计?

  • LOC delta 的定义:重构前后代码行数的净变化
  • 客观统计的意义:可度量、可对比
  • 解读 LOC delta 的注意点

LOC delta 指重构前后代码行数的净变化(新增行数减去删除行数)。它是客观、可量化的统计指标,用于统一度量重构的影响。但解读时需谨慎:行数减少不一定代表结构更好(可能只是压缩),行数增加也不一定代表变差(可能提取方法、拆分职责增加了可读性)。LOC delta 应结合复杂度、重复率等其他指标一起看,而非孤立使用。

LOC delta 的价值在于提供一个客观、可复现的度量,便于在重构报告中统一口径。但它本身不携带"质量"信息,必须结合"是否更清晰、是否降低复杂度"来解读。合理使用是把它作为众多验证指标之一,辅助判断重构对代码规模的影响,而不是作为质量好坏的唯一标准。

#
★★

14. 重构的"复杂度变化"(complexity delta)中圈复杂度的下降

什么是重构的复杂度变化(complexity delta)?如何用圈复杂度的下降衡量重构效果?

  • 复杂度变化的定义:重构前后代码复杂度的变化
  • 圈复杂度的含义与度量
  • 复杂度下降对可维护性的意义

复杂度变化指重构前后代码复杂度的差值,常用圈复杂度(Cyclomatic Complexity)度量——它计算代码中独立线性路径的数量,值越大分支越多、越难测试和理解。重构(如 Replace Conditional with Polymorphism、提取方法、拆分职责)往往使圈复杂度下降,说明分支逻辑被简化、可测试性提升。复杂度下降是可维护性改善的客观证据,是重构收益验证的重要指标之一。

圈复杂度直观反映"测试与理解的难度",且可被工具自动计算,是客观、可度量的指标。对比重构前后的圈复杂度,能量化"结构是否真的变简单"。但要注意复杂度只是维度之一,需与耦合度、重复率、可读性结合,避免单纯追求低复杂度而牺牲其他。

#
★★

15. Strangler Fig Pattern(绞杀者模式)在大型遗留系统渐进重构中的应用

什么是 Strangler Fig Pattern(绞杀者模式)?它在大型遗留系统渐进重构中如何应用?

  • 绞杀者模式的定义:用新系统逐步替换旧系统
  • 渐进替换的机制与路由控制
  • 在大型遗留系统重构中的价值

Strangler Fig Pattern 借鉴绞杀榕包围宿主树、最终取代它:在保留旧系统运行的同时,用新实现逐步替换旧系统的功能,通过路由/网关把请求按功能逐步转到新系统,每次替换一小块,验证稳定后再替换下一块,最终旧系统被"绞杀"下线。它适用于大型遗留系统无法整体重写、又必须渐进演进的场景,避免了"大爆炸"替换的高风险,让新旧系统共享过渡期、逐步迁移。

绞杀者模式的核心是"渐进、可回滚、可验证"。它把系统替换拆成多个小的、可独立交付的替换单元,每个单元都有安全网(路由开关、测试、灰度),降低风险。与 Branch by Abstraction 类似,都强调在主干上渐进演进,但绞杀者模式更常用于"整系统替换"层面的演进。它让遗留系统在"不冻结"的前提下持续演进。

#
★★

16. 重构识别与重构决策中哪些 smell 应立即修(rework)、哪些应记录债(debt)、哪些可接受(accepted)

识别到坏味道后,如何决策哪些应立即修(rework)、哪些记录为债(debt)、哪些可接受(accepted)?

  • 三类处置的划分:立即修、记录债、接受
  • 决策依据:变更频率、风险、成本、影响
  • 处置决策的实践框架

识别到坏味道后,根据"收益-成本-风险"把处置分为三类:应立即修(rework)的是那些在高变更频率区域、影响当前开发、成本低、风险小的坏味道——趁靠近该代码时顺手修复,性价比最高;记录债(debt)的是当前不值得修、但未来会遭遇的坏味道,把它们显式记录在技术债清单中,待进入活跃变更期或预算允许时再处理;可接受(accepted)的是稳定、低频变更、修复成本高风险大的坏味道,明确接受并容忍,不投入资源。决策的关键是"变更频率 × 修复紧迫性":高频且影响大的立即修,低频可缓缓,稳定且无影响的接受。

统一"立即全部修"和"全都不修"都不可取。正确的决策是弹性分区:用变更热点判断修复的紧迫性,用成本与风险判断修复的可行性,用未来演进判断是否值得投入。记录债与接受的区别在于"未来是否会遇到"——会遇到的记债,不会遇到的接受。这样把有限的精力投到收益最高的坏味道上。

#

17. 重构的"性能变化"(performance delta)中 P99 不退化

重构的"性能变化"(performance delta)指什么?为什么要求 P99 不退化?

  • 性能变化度量的定义与指标
  • P99 尾延迟的意义
  • 重构中性能验证的手段

重构的性能变化指重构前后对系统性能影响的度量,核心指标是延迟(如 P99 尾延迟)、吞吐量、资源占用。要求 P99 不退化,是因为 P99 代表最差一批请求的延迟,用户可感知的卡顿往往来自尾部延迟,且重构后结构变化可能引入额外间接层或对象创建,影响性能。通过基准测试(基准回归)对比重构前后同样负载下的 P99,若 P99 退化则说明重构引入了性能问题,需优化或回退。

P99 相比平均延迟更能反映真实用户体验,因为平均延迟会掩盖尾部劣化。重构的"行为不变"是对功能说的,性能也需保持或改善。基准测试要控制变量(相同负载、环境、数据),保证测量可比。若重构确实带来性能退化,需定位热路径,用 profiling 分析,必要时优化或权衡。

#

18. 重构的"覆盖率变化"(coverage delta)中测试覆盖的提升

什么是重构的覆盖率变化(coverage delta)?为什么重构应带来测试覆盖的提升?

  • 覆盖率变化的定义:重构前后测试覆盖率的差异
  • 覆盖率提升对重构的意义
  • 覆盖率与安全网的关系

覆盖率变化指重构前后测试覆盖率(如行覆盖、分支覆盖)的差异。重构应带来覆盖率的提升,因为重构的过程往往伴随测试先行:为被重构代码补写测试、然后重构,使原本无覆盖的代码获得覆盖,同时重构后结构更清晰也便于补充测试。覆盖率提升意味着安全网更厚,后续重构与维护更安全,形成"重构改善可测性、可测性反过来保障重构"的良性循环。

覆盖率是重构安全网强度的量化指标。重构前薄弱覆盖的代码是高风险的,重构时先补测试(特征测试)会把覆盖率提上来。但覆盖率是必要条件而非充分条件,需关注"覆盖的是否是关键路径与分支",结合分支覆盖与断言质量判断。覆盖率提升是重构收益的客观证据之一,与复杂度下降、性能不退化等指标共同构成验证体系。