重构与模式选型

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

1. 设计模式选型的决策树,根据变化维度(算法/对象创建/对象组合/行为)选择策略/工厂/组合/观察者,避免过度设计的 YAGNI 原则

说明设计模式选型的决策树,如何根据变化维度(算法/对象创建/对象组合/行为)选择策略/工厂/组合/观察者,以及如何用 YAGNI 原则避免过度设计?

  • 按变化维度选择模式的决策树
  • 策略/工厂/组合/观察者的适用场景
  • YAGNI 原则与过度设计

设计模式选型的核心是"先识别变化点,再选对应模式"。决策树按变化维度分类:若变化的是"算法",用策略模式(Strategy)把算法封装成可替换的策略对象;若变化的是"对象创建方式",用工厂模式(Factory/Abstract Factory)把创建逻辑与使用解耦;若变化的是"对象组合结构",用组合模式(Composite)统一树叶与树枝;若变化的是"对象间的状态/行为通知",用观察者模式(Observer)实现一对多订阅。选型时先问"哪里会变、变化的频率与方向",再映射到模式,而不是为了用模式而用。同时要遵循 YAGNI(你不会需要它):只有确有变化需求时才引入模式的抽象层,否则过度设计会引入多余的接口与间接层,降低可读性与维护性。

模式选型是"变化驱动"而非"模式崇拜"。先定位变化维度再选模式,能避免无脑套用;配合 YAGNI,只在真实变化点上才做抽象,既享受模式的好处又不被过度设计拖累。面试中要体现"为什么要这个模式"而非"会背这个模式"。

#
★★★

2. Replace Conditional with Polymorphism 中如何用策略/多态消除类型分支?

说明 Replace Conditional with Polymorphism(以多态取代条件表达式)重构,如何用策略/多态消除类型分支?

  • 类型分支的坏味道
  • 用多态/策略消除分支
  • 重构步骤与效果

当代码中反复出现基于类型枚举/字符串的 if-else 或 switch 分支(如按 employee.type 计算薪资),就可用 Replace Conditional with Polymorphism 重构:把每个分支的处理逻辑提取为子类的方法,用多态分发替代类型分支。具体做法是定义基类/接口,声明行为方法;为每种类型创建子类,各自实现该方法;把原分支逻辑移动到对应子类。重构后,新增类型只需新增子类,无需修改现有分支,符合开闭原则(OCP),也消除了重复的条件判断。若分支逻辑简单、类型稳定,也可用策略模式(把行为对象注入)替代,效果类似。注意:当分支是基于"值的不同"而非"类型不同"时,应改用查表或策略,而非多态。

该重构的核心是"把类型解耦为行为差异"。多态把"为每种类型写不同分支"转化为"每种类型自己实现行为",消除重复、提升可扩展性。判断何时用:分支随类型增长而频繁变化时,用多态收益最大。

#
★★★

3. 接缝(Seam)与依赖注入中如何在不改生产代码的前提下用接口、工厂或方法级注入替换依赖,让遗留代码可测

说明接缝(Seam)与依赖注入的关系,如何在不改生产代码的前提下用接口、工厂或方法级注入替换依赖,让遗留代码可测?

  • 接缝(Seam)的概念
  • 依赖注入方式(接口/工厂/方法级)
  • 让遗留代码可测试

接缝(Seam,Michael Feathers 概念)指代码中一个"可以在不改动生产逻辑的情况下替换行为"的位置,是测试遗留代码的关键。依赖注入是制造接缝的手段:把对象依赖的创建/获取从内部硬编码改为外部注入,从而在测试时替换为替身(mock/stub)。注入方式有几种:接口注入(依赖以接口类型声明,测试传入实现)、工厂注入(把依赖获取交给工厂方法,测试替换工厂)、方法级注入(把依赖作为方法参数传入,而非字段)。对遗留代码,优先在边界处找接缝(如构造函数、方法签名、工厂),用接口或方法参数引入注入,让生产代码结构不变或最小改动,就能用替身隔离外部依赖进行测试。

"接缝"回答"在哪里注入","依赖注入"回答"怎么注入"。对遗留代码,目标是在不破坏现有行为的前提下打开测试缺口。先找接缝再注入,能逐步把不可测代码变可测,是遗留系统改造的实用技巧。

#
★★★

4. Parallel Change(扩展-收缩)中新增兼容实现、双写双路径、迁移、删除旧实现的四阶段如何避免一次性断裂

说明 Parallel Change(扩展-收缩)重构,新增兼容实现、双写双路径、迁移、删除旧实现的四阶段如何避免一次性断裂?

  • Parallel Change 的概念
  • 四阶段:兼容实现-双写-迁移-删除
  • 避免一次性断裂

Parallel Change(并行变更,又称 expand-contract)是一种渐进式迁移外部接口/数据结构的方法,避免一次性破坏所有调用方。四阶段:一是"扩展/兼容"(expand):新增兼容的新实现/新接口,与旧实现并存,不破坏旧调用方;二是"双写/双路径"(run both):让新旧路径同时运行(如双写收据、双写存储),比对结果,确保一致;三是"迁移"(migrate):把调用方逐个从旧实现切换到新实现,验证新路径正确;四是"收缩/删除"(contract):所有调用方迁移完成后,删除旧实现。整个过程每一步系统都可运行、可回滚,避免"一次性切换"带来的断裂风险,常用于数据库迁移、API 版本演进、服务接口替换。

Parallel Change 的核心是"分阶段、可回滚、保持兼容"。每个阶段都有可验证的边界,减少一次性变更的风险。它在数据库列迁移、API v1→v2 等场景极其重要,体现"演进而非革命"的工程理念。

#
★★

5. Push Down Method 的适用场景中只有部分子类需要某方法时下移到子类,与方法上移(Pull Up Method)形成对称重构,如何决策?

说明 Push Down Method 的适用场景,只有部分子类需要某方法时下移到子类,与方法上移(Pull Up Method)形成对称重构,如何决策?

  • Push Down Method 的场景
  • Pull Up Method 的对称性
  • 上移/下移的决策

Push Down Method(方法下移)把基类中只有部分子类才需要的方法下移到这些子类中,避免所有子类都继承到并不适用的方法。Pull Up Method(方法上移)则相反,把多个子类共有、且行为一致的方法上移到基类,消除重复。二者是方法归属的对称重构:上移处理"重复"(多个子类同样实现→基类),下移处理"误共享"(基类方法只有部分子类适用→子类)。决策依据:看方法的"使用范围"与"行为一致性"——若所有子类都需要且行为一致,上移;若只有部分子类需要,下移;若子类实现不同但语义相同,可在基类声明抽象方法、各子类实现(模板/策略)。下移后基类更精简,子类职责更清晰。

上移/下移的目标是"方法放在最合适的位置"。判断标准是"谁用、用得是否一致"。过度上移会让基类膨胀、子类被迫继承不适用的方法;过度下移会造成重复。二者常配合使用来逐步梳理继承层次。

#
★★

6. Strangler Fig 模式在遗留系统迁移中的实施中路由层逐步切流、数据双写与一致性窗口、回滚策略、与 Branch by Abstraction 的互补关系

说明 Strangler Fig 模式在遗留系统迁移中的实施,路由层逐步切流、数据双写与一致性窗口、回滚策略,以及与 Branch by Abstraction 的互补关系?

  • Strangler Fig 的渐进迁移
  • 路由切流与数据双写
  • 回滚策略与 Branch by Abstraction 互补

Strangler Fig(绞杀者模式)指用新系统逐步"绞杀"取代遗留系统:在路由层按比例/按条件逐步把流量从旧系统切到新系统,新系统成熟后逐步扩大切流比例,最终完全替换。实施要点:路由层(网关/服务发现)支持按百分比、按用户、按功能逐步切流;数据双写保证新旧存储一致,用一致性窗口比对差异,检测迁移正确性;回滚策略:切流是可逆的,一旦发现问题立即回切旧系统,保证风险可控。与 Branch by Abstraction 互补:Branch by Abstraction 先在代码层抽象出接口,让新旧实现并存、逐步替换,是从"内部代码"角度迁移;Strangler Fig 从"外部流量与架构"角度替换,两者常结合——先用 Branch by Abstraction 在单体内抽象替换,再用 Strangler Fig 做服务级渐进切流。

绞杀者模式的价值是"渐进、可回滚、低风险"。路由切流 + 数据双写 + 一致性窗口构成迁移的安全网,回滚策略保证出问题可逆。与 Branch by Abstraction 是"外部架构级"与"内部代码级"两个互补的渐进手段。

#
★★

7. Extract Class 的重构步骤中如何识别单一职责边界、迁移字段与方法、处理双向引用,与 Extract Method 的差异?

说明 Extract Class 的重构步骤,如何识别单一职责边界、迁移字段与方法、处理双向引用,以及与 Extract Method 的差异?

  • Extract Class 的步骤
  • 识别单一职责边界
  • 处理双向引用与 Extract Method 差异

Extract Class(提炼类)把一个大类中属于不同职责的字段与方法拆到新类。步骤:先识别职责边界(哪些字段与方法属于同一职责,按 SRP 聚类);再创建新类并迁移相关字段与方法(连同它们的访问逻辑);处理双向引用(若原类与新类互相引用,需明确引用方向,必要时用接口、单向引用或反序列化避免紧耦合);最后用测试验证行为不变。与 Extract Method 差异:Extract Method 是在同一个类内把一段逻辑提炼为独立方法,属于"方法级"重构;Extract Class 是跨类拆分,属于"类级"重构。Extract Class 常先于或伴随 Extract Method,把属于新类的逻辑先提炼成方法再整体搬移。

Extract Class 的本质是"职责边界划界"。先聚类再拆分,避免拆出职责不清的类;处理双向引用是防止拆类后耦合反而更糟。与 Extract Method 是"类级"与"方法级"两个粒度的提炼,常配合使用。

#
★★

8. 重构的节奏与安全网中小步重构 + 测试保障如何配合,与大规模重写的成本对比?

说明重构的节奏与安全网,小步重构 + 测试保障如何配合,与大规模重写的成本对比?

  • 小步重构的节奏
  • 测试安全网
  • 与大规模重写成本对比

重构的节奏是"小步快走":每次只做一个小而清晰的改动,保持代码可编译、测试通过,再继续下一步。测试是安全网——在重构前建立覆盖关键行为的测试(必要时用特征测试),每次改动后运行测试验证行为未变,从而放心重构。小步重构 + 测试保障能快速定位回归、降低风险、支持持续重构。与大规模重写对比:大规模重写成本高、风险大(需重写全部功能、重新测试、可能丢失隐性需求与业务逻辑、团队耗时多),且长时间无法交付;小步重构成本低、风险可控、可随时回退、持续交付价值。因此只要可行,渐进的小步重构通常优于推倒重来;只有质量极差、结构无法修补时才考虑重写。

"小步 + 测试"是重构的安全网,让行为保持恒真。与重写对比,重构是"连续的小改变",重写是"一次性大改变"。优先重构,除非代码已无法修补。面试要体现"为什么小步比大改更安全"。

#
★★

9. 重构的评审与协作中大范围重构如何分小步合并,避免长时间分支?

说明大范围重构的评审与协作,如何分小步合并,避免长时间分支?

  • 大范围重构的分步合并
  • 避免长时间分支
  • 评审与协作

大范围重构应避免长时间分支(会带来大量合并冲突、集成风险、团队失同步),而是分小步合并到主干:把重构拆成一系列小、可独立验证的提交,每个提交保持可编译可测试,尽快合并回主干。协作上,重构涉及的改动通过小步 PR 评审,评审者关注"行为是否保持、结构是否改进";用"重构提交"与"功能提交"分离,避免评审噪音;必要时用 CI 与测试保障每一步安全。若重构确实跨多个模块,可采用"先内聚再移动"、用分支保护与频繁 merge 主干的策略,或利用 feature flags 在主干上渐进推进,避免长时间独占分支。

大重构的秘诀是"化整为零、小步合入"。长时间分支是重构的大忌,合并冲突与集成风险会吞噬重构收益。小步 PR + 频繁合主干 + 测试,让重构安全推进并保持团队同步。

#
★★

10. 重构的风险控制中小步提交、测试保护与代码评审如何保障安全?

说明重构的风险控制,小步提交、测试保护与代码评审如何保障安全?

  • 小步提交
  • 测试保护
  • 代码评审

重构的风险控制依赖三件事:小步提交(每次改动小而清晰、可回滚)、测试保护(重构前建立测试,每次改动后运行验证行为不变)、代码评审(让另一位开发者审查改动,发现遗漏与风险)。三者配合:小步提交让问题可定位、可回滚;测试保护让行为保持恒真,防止回归;评审提供第二双眼,检查重构是否引入隐藏问题或破坏意图。此外还有 CI 持续集成、特性开关、频繁合并主干等辅助手段。风险控制的目标是"行为不变、质量提升、可随时回退"。

重构的"安全"是系统工程。小步 + 测试 + 评审构成三层保障,缺一不可。面试要体现"为什么重构不是碰运气,而是受控的工程实践"。

#
★★

11. 对象间搬移行为中 Move Method 与 Move Field 如何根据耦合方向与内聚性决定搬移目标

说明对象间搬移行为,Move Method 与 Move Field 如何根据耦合方向与内聚性决定搬移目标?

  • Move Method 与 Move Field
  • 耦合方向与内聚性
  • 决定搬移目标

Move Method 与 Move Field 是对象间搬移行为的重构:把方法/字段从耦合度低、看似不相关的类,搬到它真正依赖、被高频访问的类中。决定搬移目标依据"耦合方向与内聚性":若某方法大量访问 B 类的数据(Feature Envy),应把方法搬到 B 类,让"数据与操作它的行为"内聚;若某字段主要被 B 类使用,应把字段搬到 B 类。搬移目标的选择看"谁内聚更强"——方法/字段与哪个类的关系更紧密、耦合更强,就搬到那个类。搬移后要更新所有引用,并保持行为不变,必要时用 Move Field 后临时让旧类保留访问器(accessor)以减小改动。

搬移的本质是"让行为与数据高内聚、类间低耦合"。判断"往哪搬"看耦合强度与内聚方向。搬移后代码更符合"高内聚低耦合",提升可维护性。

#
★★

12. 变异测试验证重构中为什么杀死变异体(mutation)比覆盖率更能证明行为未变

说明变异测试验证重构,为什么杀死变异体(mutation)比覆盖率更能证明行为未变?

  • 变异测试的概念
  • 杀死变异体 vs 覆盖率
  • 验证重构行为不变

变异测试(Mutation Testing)通过给代码注入变异(改变逻辑,如把 > 改成 >=、删掉一条语句、翻转条件),生成"变异体"(mutant),再运行测试:若测试能"杀死"变异体(即测试失败,说明检测出了该变异),则测试有效;若变异体存活(测试通过),说明测试没有覆盖到该逻辑,存在盲区。覆盖率只统计"哪些行被执行",无法证明"执行结果被断言";变异测试验证"行为变化能否被测试捕获",更能证明"测试真正保护了行为"。因此重构后,覆盖率高的测试可能仍无法发现行为改变,而变异测试能发现"未被有效验证"的变异,从而更可靠地证明重构保持了行为不变。

变异测试是"测试的测试",衡量测试的有效性(能否发现行为差异)。比覆盖率更强,因为它直接验证"行为变化是否被捕获"。重构时用变异测试把关,能更放心地确认行为未变。

#

13. 重构 vs 重写的决策框架中技术债务四象限(审慎/鲁莽 × 故意/无意)、何时增量重构优于推倒重来、Martin Fowler "三次法则"与 Ward Cunningham 的债务隐喻

说明重构 vs 重写的决策框架,技术债务四象限(审慎/鲁莽 × 故意/无意)、何时增量重构优于推倒重来,以及 Martin Fowler "三次法则"与 Ward Cunningham 的债务隐喻?

  • 技术债务四象限
  • 重构 vs 重写决策
  • 三次法则与债务隐喻

技术债务四象限由"是否审慎(deliberate vs reckless)× 是否故意(prudent vs inadvertent)"构成:审慎而故意(为快速交付明知会欠债)、审慎而无意(谨慎但仍有债务)、鲁莽而故意(明知会写坏还写)、鲁莽而无意(无意写坏)。决策重构 vs 重写:增量重构(小步、测试、持续)通常优于推倒重来,因为重写成本高、风险大、易丢失隐性需求;只有代码极端混乱、结构无法修补、或需要彻底技术转型时才考虑重写。Martin Fowler 的"三次法则"(Rule of Three):同一代码出现三次才值得抽象,避免过早抽象。Ward Cunningham 的债务隐喻:欠债需付利息(维护成本),但合理的债务(如审慎的鲁莽)可换取速度,需定期偿还(重构)防止利息累积。

决策框架帮你在"何时重构、何时重写"上做理性判断。债务四象限定位现状,三次法则指导抽象时机,债务隐喻强调"及时偿还"。"能重构就别重写"是默认,重写是例外。

#

14. 重构的时机与信号中重复代码、过长参数列表、发散式变化如何触发对应重构?

说明重构的时机与信号,重复代码、过长参数列表、发散式变化如何触发对应重构?

  • 重构的信号
  • 各坏味道对应的重构
  • 重构时机

重构的时机是"闻到坏味道时",且越早越好(在代码变得复杂前)。常见信号与对应重构:重复代码(Duplicated Code)→ Extract Method/Extract Class 消除重复;过长参数列表(Long Parameter List)→ Introduce Parameter Object / Preserve Whole Object 收敛参数;发散式变化(Divergent Change,一个类因多原因修改)→ Extract Class 按单一职责拆分。此外还有过长方法(→Extract Method)、数据泥团(→Extract Class)、霰弹式修改(→Move Method 收敛)等。重构时机还讲究"在提交新功能前顺手重构、在测试保护下进行、不改变外部行为"。信号出现即重构,避免债累积。

重构的触发是"坏味道",而非"计划"。每种坏味道有对应的重构手法,识别信号就能选对方向。关键是在"错误继续扩散"前及时行动,配合测试保障安全。

#

15. Branch by Abstraction 与 Strangler Fig 的区别中为什么先抽象接口再逐步替换实现能降低迁移风险?

说明 Branch by Abstraction 与 Strangler Fig 的区别,为什么先抽象接口再逐步替换实现能降低迁移风险?

  • Branch by Abstraction 与 Strangler Fig 区别
  • 先抽象接口再替换实现
  • 降低迁移风险

Branch by Abstraction(抽象分支)是在代码层先引入一个抽象接口,把新旧实现都挂在接口下,通过配置/开关切换,逐步把调用方从旧实现迁到新实现,最后删除旧实现。Strangler Fig(绞杀者)是在架构/路由层用新系统逐步替换旧系统,通过路由切流逐步转移流量。区别:Branch by Abstraction 是"单体内部、代码级"的渐进替换;Strangler Fig 是"系统间、架构级"的渐进替换。先抽象接口再逐步替换能降低迁移风险,因为:接口隔离了新旧实现,调用方不感知内部变化;每一步切换都是独立、可回滚的;新旧实现可并存做对比验证;迁移失败可随时回切,不会一次性断裂。

两者都是"渐进迁移",但层次不同:代码级 vs 架构级。先抽象接口的价值是"隔离变化、可回滚、可验证",让迁移风险被控制在每一步小切换里。二者常结合使用。

#

16. 反模式识别与纠偏中 God Class、Spaghetti Code、Shotgun Surgery、Feature Envy 的代码坏味道特征,以及如何用重构手法(Move Method/Extract Class/Introduce Parameter Object)系统性消除

说明反模式识别与纠偏,God Class、Spaghetti Code、Shotgun Surgery、Feature Envy 的代码坏味道特征,以及如何用 Move Method/Extract Class/Introduce Parameter Object 等重构手法系统性消除?

  • 各反模式的特征
  • 对应重构手法
  • 系统性消除

常见反模式及其特征:God Class(上帝类)——一个类承担过多职责、字段方法众多、LCOM 低;Spaghetti Code(面条代码)——逻辑纠缠、顺序混乱、依赖深、难以理解;Shotgun Surgery(霰弹式修改)——一个需求改动要改多处文件;Feature Envy(依恋情结)——方法过度访问其他类数据。纠偏手法:God Class 用 Extract Class 按职责拆分;Spaghetti Code 用 Extract Method 捋清流程、拆分为清晰步骤;Shotgun Surgery 用 Move Method 把散落逻辑收敛到一处;Feature Envy 用 Move Method 把方法移到它依赖数据的类。系统性做法是"先识别坏味道模式,再映射到对应重构手法,逐个消除",配合测试验证行为不变。

反模式识别是"对症下药"的前提。每种坏味道有明确特征与对应重构,识别准才能选对手法。系统性消除要"逐个、小步、有测试",避免引入新问题。

#

17. 微服务上下文中的重构策略中单体拆分时的 DDD 限界边界识别、防腐层(ACL)隔离、数据库拆分的事务一致性过渡方案(dual-write → CDC → 单写)

说明微服务上下文中的重构策略,单体拆分时的 DDD 限界边界识别、防腐层(ACL)隔离,以及数据库拆分的事务一致性过渡方案(dual-write → CDC → 单写)?

  • DDD 限界上下文识别
  • 防腐层(ACL)隔离
  • 数据库拆分的过渡方案

单体拆分微服务时,先用 DDD 识别限界上下文(Bounded Context),即按业务职责划分边界,确定哪些模块属于一个服务、模块间如何协作,避免按技术分层硬切。防腐层(Anti-Corruption Layer,ACL)用于隔离—在每个服务边界放置适配层,把外部系统的模型转换为本服务的领域模型,防止外部概念污染内部领域,降低服务间耦合。数据库拆分的一致性过渡:直接把共享库拆了会破坏事务一致性,常用渐进方案——先 dual-write(双写:新服务与旧系统同时写两份数据),再 CDC(Change Data Capture:通过解析 binlog 同步增量数据,把旧库变从库),最后单写(旧库退役,完全由新服务写)。这样迁移期间保持一致性,可回滚。

微服务重构的难点是"边界划分"与"数据一致性"。DDD 定边界、ACL 隔离耦合、dual-write→CDC→单写过渡数据,是渐进、可回滚的拆分路径。面试要体现迁移的"渐进与一致性保障"。

#

18. 常用重构手法中 Extract Method、Move Method、Replace Conditional with Polymorphism 的步骤?

说明常用重构手法 Extract Method、Move Method、Replace Conditional with Polymorphism 的步骤?

  • Extract Method 步骤
  • Move Method 步骤
  • Replace Conditional with Polymorphism 步骤

Extract Method(提炼方法):① 识别一段内聚的逻辑代码;② 给它起一个表达意图的名字,作为新方法;③ 把这段代码移到新方法,原位置改为调用;④ 把用到的局部变量作为参数或返回值传递;⑤ 运行测试验证行为不变。Move Method(搬移方法):① 识别方法真正依赖的类;② 在目标类中创建同逻辑的方法;③ 让原方法转发或删除,更新调用方;④ 必要时连带 Move Field;⑤ 测试验证。Replace Conditional with Polymorphism(多态取代条件):① 找到基于类型的分支;② 定义基类接口/抽象方法;③ 为每种类型建子类并实现该方法;④ 把分支逻辑移到子类;⑤ 用多态调用替换分支,测试验证。

这三种是高频重构,步骤核心都是"识别-新建-迁移-替换-测试"。掌握步骤能应对手撕重构题,也体现"行为保持"的工程素养。

#

19. 模式选型的陷阱中什么时候引入模式是过度设计,如何用"需求变化点"判断?

说明模式选型的陷阱,什么时候引入模式是过度设计,如何用"需求变化点"判断?

  • 模式选型的陷阱
  • 过度设计的判断
  • 需求变化点

模式选型的陷阱是"为了用模式而用模式"——引入与当前需求无关的抽象层、接口、工厂,导致代码复杂度上升、可读性下降,却没有实际收益,即过度设计。判断是否过度设计,核心用"需求变化点":问"这个模式所抽象的变化,当前是否真的会发生?"若某个抽象对应的变化点(如算法替换、类型扩展、创建方式变化)当前没有迹象、未来也未必发生,则引入是过度设计;若确有明确变化需求,模式才值得。此外,YAGNI 原则提醒:不为假设的未来需求预建抽象。判断标准还有"抽象是否只有一个实现""参数是否从不变更"等信号。

模式是"应对变化"的工具,不是"装饰"。用"需求变化点"判断:有真实变化需求才引入模式,否则宁简勿繁。避免过度设计的本质是"抽象服务于已验证的需求"。

#

20. 重构中的行为保持中每步重构都应保持可编译可测试,如何用特征测试(characterization test)验证行为不变?

说明重构中的行为保持,为什么每步重构都应保持可编译可测试,以及如何用特征测试(characterization test)验证行为不变?

  • 重构行为保持
  • 可编译可测试
  • 特征测试

重构的原则是"行为不变",每一步都应保持可编译、可测试,因为:可编译保证改动不破坏语法与类型,可测试保证行为逻辑未被破坏,两者把问题定位到"最近一步",避免错误累积。特征测试(characterization test,又称 characterization test)用于没有现成测试的遗留代码:先用输入输出记录当前行为,把"当前实际行为"固化为测试断言,即使该行为可能不是预期,也先锁定,重构后运行验证行为不变。这样重构可在"行为未变"的前提下安全进行,若后续要改行为,再单独修改测试。

"行为保持"是重构的底线。可编译可测试让每步可验证,特征测试为无测试的遗留代码建立安全网。推荐做法是"先固化行为,再重构,再验证"。

#

21. 删除死代码中如何用调用图与版本控制安全识别不可达代码,为什么直接删比注释保留更好

说明删除死代码的方法,如何用调用图与版本控制安全识别不可达代码,以及为什么直接删比注释保留更好?

  • 死代码识别
  • 调用图与版本控制
  • 直接删 vs 注释保留

死代码(不可达/无引用的代码)用调用图识别:静态分析构建调用图(谁调用谁),从未被调用的方法/类即为候选死代码;还可结合编译器/IDE 的"未使用"警告、运行期覆盖率(某代码从未执行)辅助确认。删除前用版本控制保障安全:git 历史完整保留,删除后如需可随时恢复。直接删比注释保留更好,因为:注释掉的代码会误导读者、与现逻辑失步、增加噪音;而版本控制已提供恢复能力,无需注释兜底。删除后代码更干净、无歧义,且避免读者误判"可能是死代码"。

死代码删除的关键是"安全识别"与"可恢复"。调用图 + 静态分析确认不可达,版本控制保证可回退。直接删优于注释,体现"版本控制是删除的安全网"。