Skip to content

Latest commit

 

History

History
123 lines (73 loc) · 7.22 KB

File metadata and controls

123 lines (73 loc) · 7.22 KB

3 代码的坏味道

3.1 重复代码

如果项目内多个地方看到了相同的代码, 那就想办法将其合为一

3.2 过长函数

多对大函数进行分解, 每当需要长注释的时候就应该将所需的分段进行包装了, 有时候替换后的函数只包含一行代码也没关系.

注释, 条件表达式和循环, 都是提炼代码的信号. 提炼的时候可能会产生过长的参数列表, 考虑如何将长参数包装为一个参数对象进行传递

3.3 过大的类

和大函数一样, 当某个类负责了太多内容时就会产生冗余和混乱, 最好按照类所进行的工作为每个方法都提炼出接口, 然后慢慢分解

3.4 过长参数列

太长的参数列难以理解, 太多参数会造成前后不一致、不易使用, 且一旦需要更多数据就不得不修改它. 因此可以包装一个足够全面的参数类, 然后让目标函数自己从参数对象中获取自己需要的参数

但是有时候不希望两个对象由于大量和互相使用而耦合, 那种时候还是需要适当提炼为函数, 但是还是要保持参数列别太长

3.5 发散式变化

我们希望软件能够更容易被修改, 一旦需要修改, 我们希望能够跳到系统的某一点, 只在该处做修改. 如果不能做到这点, 一个类由于外界发生对变化需要进行不同部分对修改时, 发散式变化发生了

这种时候我们应该将这个类进行拆分, 另外界某一功能的修改产生的变化之发生在一个类中

3.6 霰弹式修改

和3.5类似, 但这是一个外界变化产生的修改发生在各处. 这种问题需要将所有需要修改的代码整合为一个类集中修改, 如果眼下没有合适的类那就创建一个

3.7 依恋情结

如果一个函数高度依赖多个类的属性, 那么应该判断哪个类被这个函数使用得最多, 然后将函数放到这个类中

这也就是核心:总是将一起变化的东西放到一起, 保持变化之在一处发生

3.8 数据泥团

总是绑定在一起使用的数据应该拥有属于自己的对象, 判断方法就是删除这堆数据中某个, 看看这堆数据是不是一起失去了意义, 失去了就代表该绑定了

3.9 基本类型偏执

不要吝啬使用小对象, 将一些基本类型包装为类很实用

3.10 Switch惊悚现身

switch的问题在于重复, 相近的switch常常遍布程序各处. 而我们使用switch的目的常常是达到一种简单的多态, 那更好的选择就是将switch提取为一个函数然后将其放到可以复用的类里

3.11 平行继承体系

这是3.6的特殊情况, 我们可能在某个时候发现修改需要发生在两个平行继承的类中(霰弹创建的多个平行类), 此时可以让一个继承体系的类的实例去引用另一个继承体系的实例, 避免产生新的麻烦

3.12 冗赘类

尽管我们需要分解代码来保持逻辑的清晰, 但是一旦我们发现某些类的存在是不必要的, 徒增了理解的难度, 那就要及时将其删除

3.13 夸夸其谈未来性

不要过度设计, 绝大多数预先设计都是无用的. 如果我们发现某个设计除了在测试样例中外毫无作用, 那么我们应该讲这个设计连同这个测试样例一起删掉

3.14 令人迷惑的暂时字段

如果一个类中有一个复杂算法, 需要好几个临时变量的协作, 那么我们应该将这些操作和变量拆分提取到一个独立类中, 提炼后称为函数对象. 由这个类去维护那些临时变量, 也方便我们传递这些变量

3.15 过度耦合的消息链

长长的消息链并不一定是坏事, 有时候是被逼无奈的. 重要的是防止消息链过度耦合, 使得一个小小的修改影响了整个链的运作. 我们应该将消息链尽量提取和拆分, 提炼一些小函数作为链条中间的接口, 当用户可以从链的任何节点开始运行时, 解耦就做得差不多了

3.16 中间人

避免太多的委托和中间人的设计, 如果发现某个类和另一个类的交流中有一半以上的接口都由中间人来负责的话, 不如将中间人的相应实现提回对话的两边, 然后消除这个无用的中间人. 中间人应该只负责一点点粘合工作, 两个类间如果可以的话尽量不要中间人

3.17 狎昵关系

不要让两个类过于亲密, 大量的private都能互相访问, 我们应该将这种耦合的类的相关方法尽量提取到新的类中, 然后让这两个类一起使用中间类来交互.

相似的, 如果子类对父类在继承中有了过多的了解, 也应该用委托来减少过多的试探, 和3.16似乎有冲突/取舍, 3.17这里主要是针对访问private的问题

3.18 异曲同工的类

如果出现两个函数做着一样的事情但是名称和接口不太一样, 应该尽量将其整合为一个函数, 或者用一个类来包装

3.19 不完美的库类

库类的设计有时候也有一些不好的问题, 如果只想修改库类的一两个函数, 可以运用后面的Introduce Foreign Method方法;如果想要添加一大堆额外行为, 就得运用Introduce Local Extension.

3.20 纯稚的数据类

用来管理字段的数据类很好, 但是我们要注意对其进行良好的封装, 尽量少暴露其内部的接口, 并为其设计一些非常常用的功能以物尽其用

3.21 被拒绝的馈赠

传统思想中常常告诉我们超类应该是抽象的, 但是这并不是很大的问题, 我们常常需要继承来服用父类的方法, 所以没必要保持抽象. 而且如果遇到了我们需要继承一个类, 可是这个类中有些接口我们不想继承, 不要立刻为父类创建兄弟类, 我们只需拒绝继承超类的实现即可, 其实接口的多余继承并不重要

3.22 过多的注释

注释是一种好习惯, 但是当感觉需要撰写注释时, 先尝试重构试着让所有注释都变得多余, 通过提炼函数, 重命名各种函数和变量, 用断言替代注释中的规格声明. 注释的良好运用时机是记述将来的打算和自己都没有十足把握的区域. 这类信息可以帮助将来的修改者, 尤其是将来的自己.