读完了重构,感觉受益匪浅,准备写一个系列的文章。
这篇讲代码的坏味道,学习它是为了提升我们的眼力,为找到对应的重构方法做准备
笔记
缺少封装
全局数据(Global Data)
症状: 全局作用域的变量, 类变量, 单例
分析: 从代码库的任何一个角落都可以修改它,而且没有任何机制可以探测出到底哪段代码做出了修改。最刺鼻的坏味道之一。
可变数据(Mutable Data)
症状: 将可变对象作为函数参数
分析: 难以追踪数据变更, 定位故障
过长参数列表(Long Parameter List)
症状: 函数需要的参数很多, 如4个以上的必填参数
分析: 额外损耗调用者的心智, 也是函数违背了单一职责的征兆.
基本类型偏执(Primitive Obsession)
症状: 用基本类型来代表业务对象, 如钱、坐标、范围、人名等;
分析: 缺少必要的抽象, 容易诱发数据泥团, 也容易产生重复代码.
过长的消息链(Message Chains)
症状: 超长的链式调用, 或者一长串取值函数, 或一长串临时变量.
分析: 系统难理解和维护, 客户端代码将与查找过程中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。
不当封装
发散式变化(Divergent Change)
症状: 一处代码包含了多个上下文, 也就是承担了多个职责.
分析: 难以测试; 增大修改的难度, 因为要关注多个上下文.
霰弹式修改(Shotgun Surgery)
症状: 一个上下文, 被分散在多处, 每遇到某种变化,你都必须在许多不同的类\函数\模块内做出许多小修改.
分析: 与发散式变化类似, 难以测试, 难以修改.
依恋情结(Feature Envy)
症状: 一个函数跟另一个模块中的函数或者数据交流格外频繁,远胜于在自己所处模块内部的交流.
分析: 错误的抽象, 导致不同上下文的交汇, 难以测试.
内幕交易(Insider Trading)
症状: 不同模块之间互相调用过多
分析: 说明这两个模块的封装不恰当, 模块的函数或者数据放错地方了.
缺少抽象
重复代码(Duplicated Code)
症状: 在一个以上的地点看到相同的代码结构
分析: 影响可读性, 阅读时需要花额外的精力,比较多个地方是否存在差异。
过长函数(Long Function)
症状: 函数很长(超过100行), 大量的参数和临时变量, 复杂的循环和分支
分析: 影响可读性, 逻辑复杂难以测试
数据泥团(Data Clumps)
症状: 扎堆出现的多个数据项, 删掉众多数据中的一项, 其他数据就失去了意义; 例如: 多个类的相似属性, 多个函数的多个相同参数.
分析: 缺少必要的抽象, 开发者关注了过多细节; 难以修改
重复的switch (Repeated Switches)
症状: 出现在多个地方的相同switch语句, 或者连续的if\else.
分析: 是重复代码的一种, 也会引发霰弹式修改; 容易引发bug
循环语句(Loops)
症状: 过多的循环, 嵌套的循环
分析: 难以一眼看出循环代码的意图, 常演变成在一个循环里做多件事情.
过大的类(Large Class
症状: 单个类做太多事情, 有些字段或者函数关联性不高
分析: 与过长函数类似, 违背了单一职责原则.
过度抽象
冗赘的元素(Lazy Element)
症状: 可有可无的代码元素; 如: 只有一个方法的类, 只有一个子类的父类.
分析: 过度设计的典型, 难以应对变化. 如无必要, 勿增实体.
夸夸其谈通用性(Speculative Generality)
症状: 为了通用性而预先设计的代码结构, 实际上去很少用到; 如: 预留许多钩子处理各种非必要的事情, 无实际意义的多层继承体系.
分析: 过度设计, 系统更难理解和维护。
临时字段(Temporary Field)
症状: 预留的字段或者变量, 仅为某种特定情况而设.
分析: 过度设计, 代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有字段。
中间人(Middle Man)
症状: 没有必要的中间人对象, 过度使用委托. 如: 某个类的接口有一半的函数都委托给其他类.
分析: 过度设计
不当抽象
异曲同工的类(Alternative Classes with Different Interfaces)
症状: 在不同继承体系的类, 做的事情却大同小异
分析: 重复代码的一种类型
纯数据类(Data Class)
症状: 只有数据字段和读写函数的类
分析: 纯数据类常常意味着行为被放在了错误的地方, 使得难以追踪数据的修改.
被拒绝的遗赠(Refused Bequest)
症状: 子类和父类的相似度很低; 如: 子类只需要父类的一个方法, 却继承了整个类.
分析: 意味着继承体系设计错误
其他
神秘命名(Mysterious Name)
症状: 不能清晰地表明自己的功能和用法, 或者随意地取无意义的名称.
分析: 对可读性有很大影响, 如果你想不出一个好名字,说明背后很可能潜藏着更深的设计问题。
注释(Comments)
症状: 超长的的注释
分析: 意味着相关代码实现难以理解
思维导图
坏味道与重构手法速查表
坏味道(英文) | 坏味道(中文) | 原书页码 | 常用重构 |
---|---|---|---|
Alternative Classes with Different Interfaces | 异曲同工的类 | 83 | 改变函数声明(124),搬移函数(198),提炼超类(375) |
Comments | 注释 | 84 | 提炼函数(106),改变函数声明(124),引入断言(302) |
Data Class | 纯数据类 | 83 | 封装记录(162),移除设值函数(331),搬移函数(198),提炼函数(106),拆分阶段(154) |
Data Clumps | 数据泥团 | 78 | 提炼类(182),引入参数对象(140),保持对象完整(319) |
Divergent Change | 发散式变化 | 76 | 拆分阶段(154),搬移函数(198),提炼函数(106),提炼类(182) |
Duplicated Code | 重复代码 | 72 | 提炼函数(106),移动语句(223),函数上移(350) |
Feature Envy | 依恋情结 | 77 | 搬移函数(198),提炼函数(106) |
Global Data | 全局数据 | 74 | 封装变量(132) |
Insider Trading | 内幕交易 | 82 | 搬移函数(198),搬移字段(207),隐藏委托关系(189),以委托取代子类(381),以委托取代超类(399) |
Large Class | 过大的类 | 82 | 提炼类(182),提炼超类(375),以子类取代类型码(362) |
Lazy Element | 冗赘的元素 | 80 | 内联函数(115),内联类(186),折叠继承体系(380) |
Long Function | 过长函数 | 73 | 提炼函数(106),以查询取代临时变量(178),引入参数对象(140),保持对象完整(319),以命令取代函数(337),分解条件表达式(260),以多态取代条件表达式(272),拆分循环(227) |
Long Parameter List | 过长参数列 | 74 | 以查询取代参数(324),保持对象完整(319),引入参数对象(140),移除标记参数(314),函数组合成类(144) |
Loops | 循环语句 | 79 | 以管道取代循环(231) |
Message Chains | 过长的消息链 | 81 | 隐藏委托关系(189),提炼函数(106),搬移函数(198) |
Middle Man | 中间人 | 81 | 移除中间人(192),内联函数(115),以委托取代超类(399),以委托取代子类(381) |
Mutable Data | 可变数据 | 75 | 封装变量(132),拆分变量(240),移动语句(223),提炼函数(106),将查询函数和修改函数分离(306),移除设值函数(331),以查询取代派生变量(248),函数组合成类(144),函数组合成变换(149),将引用对象改为值对象(252) |
Mysterious Name | 神秘命名 | 72 | 改变函数声明(124),变量改名(137),字段改名(244) |
Primitive Obsession | 基本类型偏执 | 78 | 以对象取代基本类型(174),以子类取代类型码(362),以多态取代条件表达式(272),提炼类(182),引入参数对象(140) |
Refused Bequest | 被拒绝的遗赠 | 83 | 函数下移(359),字段下移(361),以委托取代子类(381),以委托取代超类(399) |
Repeated Switches | 重复的switch | 79 | 以多态取代条件表达式(272) |
Shotgun Surgery | 霰弹式修改 | 76 | 搬移函数(198),搬移字段(207),函数组合成类(144),函数组合成变换(149),拆分阶段(154),内联函数(115),内联类(186) |
Speculative Generality | 夸夸其谈通用性 | 80 | 折叠继承体系(380),内联函数(115),内联类(186),改变函数声明(124),移除死代码(237) |
Temporary Field | 临时字段 | 80 | 提炼类(182),搬移函数(198),引入特例(289) |