跳到主要内容

refactoring-2e 切分大纲(12 章)

切分原则:第 1–4 章对着原书 1–4 章(叙事部分),第 5–11 章把原书第二部分手法名录(第 6–12 章) 按「什么时候该用哪类手法」的决策主线重组,不逐手法罗列;第 12 章收回原书第 2 章后半 (挑战/架构/性能/工具)+ 全书收束。原书第 5 章(介绍名录)并入第 5 章开头作「名录怎么读」。

01-refactoring-first-example.md 一场手术看全程:重构是什么

  • §1 讲什么:原书用一个 44 行的账单函数开书,我们同走一遍。
  • §2 全景:戏剧团账单程序(输入/输出/三条计费规则),44 行单函数。
  • §3 为什么要动它:两个马上要来的需求(HTML 输出、新剧种)如何暴露「能跑但改不动」。
  • §4 主走查(原书全程):提炼 amountFor → 改名/去临时变量 → 拆分阶段 → 多态计算器,每步带具体数($650.00 / 47 credits / 40000 美分…)与小步节奏(编译-测试-提交)。
  • §5 节奏与性能异议:查找 1→3 次、循环拆两遍,为什么先不管。
  • §6 判断与边界:小例子不配重构?好代码的客观标准;44→70 行的账怎么算。
  • §7 可带走 + 原文地图。

02-what-is-refactoring.md 重构的定义:小步、行为保持、两顶帽子

  • §1 讲什么:把第 1 章的「手感」变成可检验的定义。
  • §2 名词与动词:重构(名词)=一种结构调整,重构(动词)=用一串小步骤调整;「一两天不可用」判据。
  • §3 「可观察行为」的确切含义:性能会变、接口会变、bug 留着。
  • §4 两顶帽子:加功能与调结构不同时做;与「先设计后编码」之争,设计浮现。
  • §5 为什么值得:改进设计/更易理解/帮找 bug/开发更快;设计耐久性假说(作者自认假说)。
  • §6 何时动手:三次法则、预备性重构(先向北 20 公里上高速)、帮助理解、捡垃圾、代码复审;何时不该动:不修改的丑代码、重写更容易。
  • §7 判断与边界:重构不是道德,是经济账(作者原话「银钳子」);与性能优化的分界。

03-code-smells.md 坏味道:什么时候该重构的气味罗盘

  • §1 讲什么:「知道怎么改」≠「知道何时改」,Beck 的味道比喻。
  • §2 罗盘怎么用:不给量度,只给直觉的支架;味道↔手法速查的用法。
  • §3 按决策聚类一:名字与重复(神秘命名、重复代码)。
  • §4 聚类二:函数装不下(过长函数、过长参数列表)——「注释=语义距离」判据。
  • §5 聚类三:数据不设防(全局数据、可变数据、基本类型偏执、数据泥团、纯数据类)。
  • §6 聚类四:变化的方向错了(发散式变化 vs 霰弹式修改,互为镜像)。
  • §7 聚类五:东西放错了地方(依恋情结、内幕交易、消息链、中间人、过大的类、异曲同工)。
  • §8 聚类六:为不存在的未来造东西(夸夸其谈通用性、冗赘元素、临时字段、被拒绝的遗赠)。
  • §9 聚类七:switch、循环与注释(2 版从「switch」改为「重复的 switch」的立场变化)。
  • §10 判断与边界:味道是启发式不是罪名;十有八九很淡就不值得理睬。

04-self-testing-code.md 测试网:小步敢迈的前提

  • §1 讲什么:重构每一步的保险丝从哪来。
  • §2 自测试代码的来历与价值:bug 必在「上次全绿之后改的那几行」——查错范围小到几行。
  • §3 主走查(Province 生产计划):sampleProvinceData(Asia,demand 30,price 20)→shortfall 5→故意改坏→0 passing 1 failing→夹具 beforeEach→改 production=20→shortfall -6/profit 292→边界(空生产商/0/负值/空串→NaN)。
  • §4 怎么写:夹具独立(const 只锁引用)、先随便填期望值再回填真实值、每个测试至少失败一遍。
  • §5 测多少:风险驱动不测访问函数;覆盖率不是质量;收到 bug 先写复现测试。
  • §6 判断与边界:测试也可能过度;「不能证明没有 bug」不妨碍「提高编程速度」。

05-first-group-moves.md 第一组手法:从看不进门到拆成模块

  • §1 讲什么:原书名录怎么读(名称/速写/动机/做法/范例五段式;婴儿学步),以及本章的决策主线:一段代码怎么一步步获得结构。
  • §2 看不懂→提炼函数/提炼变量:「意图与实现分开」判据;6 行就开始臭味;返回值命名 result。
  • §3 名字碍事→内联:函数体与名字一样清晰、间接层没有价值时退回去。
  • §4 关节不对→改变函数声明:简单做法 vs 迁移式做法(deprecated 转发),published interface。
  • §5 数据要搬→先封装变量:把「改数据」变成「改函数」;不可变性是防腐剂。
  • §6 参数结伴/函数结伴→引入参数对象、组合成类/组合成变换:更新源数据选类,只读选变换。
  • §7 两件事→拆分阶段:编译器模型,中转数据结构;主走查沿用 01 章 renderPlainText/createStatementData。
  • §8 判断与边界:一条手法不是一套做法;小步是大步子的前提。

06-encapsulation.md 封装:给每个可变数据装一道门

  • §1 讲什么:封装不是「私有字段」,是控制修改的入口;门装到多深,按数据被多少人碰。
  • §2 第一道门:封装变量(引出 05 章预告过的机制细节)。
  • §3 记录与集合:封装记录(存储 vs 计算的字段)、封装集合(返回副本/只读,别返回裸集合)。
  • §4 从裸值到对象:以对象取代基本类型(Priority 类生长史)。
  • §5 责任的分与合:提炼类/内联类(什么时候开第二道门、什么时候拆掉)。
  • §6 封装的程度是个旋钮:隐藏委托关系↔移除中间人(迪米特法则的吐槽)。
  • §7 最后一招:替换算法。
  • §8 判断与边界:过度封装的代价;同一代码库做法要一致。

07-moving-features.md 搬移:把代码送到它归属的上下文

  • §1 讲什么:归属判错是一切错位的根;这一章全在「搬家」。
  • §2 搬移函数/搬移字段:依恋谁就搬向谁;数据结构是根基但很难一次做对。
  • §3 语句级搬家:移动语句(副作用判据)、搬移语句到函数(消除重复)、搬移语句到调用者(抽象边界漂移)。
  • §4 循环的两步处理:拆分循环(一事一循环)→以管道取代循环(filter/map)。
  • §5 以函数调用取代内联代码:外表相似≠真重复。
  • §6 移除死代码:版本控制时代的删除伦理。
  • §7 判断与边界:决定越难做,搬不搬越不重要;先安置再观察。

08-organizing-data.md 重新组织数据:一个变量只干一件事

  • §1 讲什么:混乱常从数据开始:一值多用、派生不同步、引用值不分。
  • §2 拆分变量:赋值两次=两个责任;循环变量/收集变量例外;苏格兰布丁走查(acc→primary/secondaryAcceleration)。
  • §3 派生变量→查询:可变数据是错误之源;引入断言先验证「能即时算」。
  • §4 引用对象 vs 值对象:更新方式之别(改属性 vs 整个换);何时选哪边。
  • §5 值→引用:共享数据要更新时,仓库(repository)登场;「重复的对象让我紧张」。
  • §6 判断与边界:字段改名的前提是封装;全局仓库是强力药物。

09-conditionals.md 条件逻辑:分支的形状决定手法

  • §1 讲什么:复杂度大多来自条件逻辑;先看分支「是什么性质」再选手法。
  • §2 一眼读不懂→分解条件表达式;检查各不同结果相同→合并(把「做什么」换成「为什么」)。
  • §3 一支是异常→卫语句:给分支不同的重视程度;单一出口教条的终结。
  • §4 分支按类型裂开→以多态取代条件表达式:鸟例子走查(swallow 35 / 40−2×coconuts / voltage);「所有条件都该多态」是作者明确反对的。
  • §5 到处检查同一个特殊值→引入特例:Null 对象是特例的特例。
  • §6 隐含假设→引入断言:断言是交流,行为保持。
  • §7 判断与边界:2 版从「switch 语句」改成「重复的 switch」意味着什么。

10-refactoring-api.md API 的关节:谁承担获取数据的责任

  • §1 讲什么:API 是连接骨肉的关节;这一章的决策主线是「责任放在调用方还是被调方」。
  • §2 查询与修改分离:CQS 原则与作者「尽量遵守但不绝对」;缓存为何不算副作用。
  • §3 假差异:移除标记参数(true 是什么意思?);字面量+影响控制流才算标记。
  • §4 参数的进出:保持对象完整(传整对象)、以查询取代参数 vs 以参数取代查询(「同样容易」判据、引用透明性)。
  • §5 收紧创建口:移除设值函数(构造后不可变)、以工厂函数取代构造函数(构造函数的三大局限)。
  • §6 函数太大→命令对象:95% 情况选函数。
  • §7 判断与边界:参数列表应总结可变性;决策不会一劳永逸。

11-inheritance.md 继承:先用,坏了再换成委托

  • §1 讲什么:最后一批手法,处理「相似但又不同」;主线是继承的三道判据。
  • §2 共性上提:函数/字段上移、构造函数本体上移、提炼超类(继承在演化中浮现)。
  • §3 差异下沉:以子类取代类型码(多态+特有字段两个诱人处;直接子类 vs 包装类型码)。
  • §4 反向修剪:移除子类、折叠继承体系(子类有成本)。
  • §5 继承的三道判据与退路:只能打一次的牌;超类改动破坏子类;stack extends list 与车模汽车(类型与实例名不符实)→以委托取代子类/取代超类。
  • §6 主走查:鸟例子的两次变身(子类版→委托版 SpeciesDelegate),同一批数字看结构变化。
  • §7 判断与边界:「组合优于继承」的确切含义;先继承后委托不是和稀泥。

12-refactoring-in-the-real-world.md 真实世界:重构如何在团队里活下来

  • §1 讲什么:前面假定「你能随便改代码」;这一章收回原书第 2 章后半——进度压力、所有权、分支、遗留代码、数据库、性能、工具。
  • §2 进度与经济账:「重构拖慢进度」是最大阻力;不要用道德论证重构;对经理说不告诉也行(作者原话)。
  • §3 所有权与分支:published interface 约束;特性分支越长合并越难(语义冲突),CI/基于主干开发;为什么重构特别需要 CI。
  • §4 遗留代码与数据库:没测试的遗留系统→《修改代码的艺术》接缝法;数据库重构=迁移脚本+并行修改(expand-contract)。
  • §5 性能三法与 90% 统计:预算法/持续关注/热点调优;Ron Jeffries 五分钟翻倍的故事(先度量,别臆测)。
  • §6 YAGNI 与架构:灵活性机制是猜测;「以后再重构有多难」作为决策判据。
  • §7 工具:从文本替换到语法树到语言服务器;静态类型与动态类型的差别(兼谈 JS 版与 Java 版示例的差异)。
  • §8 全书收束:三个声音(作者/编者注/熊节导读)、匠艺立场;我们的判断。