Skip to content

24种代码坏味道,这些坏味道当中,我们既需要在编码的时候规避,同时也要熟悉相应的重构手法来消灭坏味道。

神秘命名

当我们不能给一个模块,一个对象,一个函数,甚至一个变量找到合适名称的时候,往往说明我们对问题的理解还不够透彻,需要重新去挖掘问题的本质,对问题域进行重新分析和抽象。

重复代码

阅读重复代码时,需要加倍仔细,修改时需要找到所有副本来修改。

同一类的两个函数含有相同的表达式,就应该提炼。

过长函数

函数过长,难以理解,且高耦合,不利于拆解重组。最好的程序,其中函数一般都很短。

如果你觉得需要写注释,大部分情况就代表这个东西需要写进一个独立的函数里面,然后根据用途来命名比较好。 条件表达式和循环往往也是提炼函数的信号。

过长参数列表

参数列表过长,每次调用都需要去看函数的实现。

  • 把标记参数移除,取而代之的是几个语义更加清晰的函数
  • 使用类可以有效的缩短参数列表。如果多个函数有同样的几个参数,引入一个类就尤为有意义。(可以用类的属性来存数据,方法来操作数据)

全局数据

全局的数据避免直接修改,没有机制探测出是哪发生了修改。

正确的做法是将全局数据封装起来,用函数将其包起来,这样就知道那些地方修改了它。

有少量的全局数据或者无妨,但数量越多,处理难度就会指数上升。(良药与毒药的区别在于剂量)

可变数据

可变数据通常指的是代码中存在频繁修改的可变状态或变量。这可能导致代码难以理解和维护。

核心是缩小作用域。可以通过封装变量来确保所有数据更新操作都通过很少几个函数来进行,使其更容易被监控。

发散式变化

发散式变化指某个模块因为不同的原因在不同的方向上发生变化。

每次只关心一个上下文。找到引起发散式变化的原因,将它拆分出来。 将会发生变化的每个子模块封装成一个函数,当他们改变时,只需要修改子模块的函数

霰弹式修改

每遇到某种变化,都需要在许多不同的类内做出许多小修改

重构手法包含:

  • 搬移字段
  • 搬移方法
  • 函数组合成类
  • 函数组合成变换
  • 拆分阶段
  • 内联函数
  • 内联类

依恋情节

两个模块(如:类)的数据交流(访问)格外频繁,远超模块内部。

解决方法: 搬移功能,将数据交流紧密的部分放到一个模块中

数据泥团

类里面把存储了太多数据,有些数据只会一起出现

重构手法:这种数据应该定义他们自己的对象

基本类型偏执

很多程序员不愿意创建对自己的问题域有用的基本类型,如钱,坐标,范围等。

比如有程序员用字符串来表示电话号码,实际上你应该抽象出来一个电话号码对象。

如果类里面一个数据太多的操作逻辑, 可以将这个数据以及操作单独封装一个类。

重复的 switch

尽量使用多态,而非switch

使用工厂函数创建实例方法,子类继承父类,子类重写父类的条件逻辑函数,修改和增添特性都变得简单了

循环语句

我们应该用管道操作(如filter和map)来替代循环,这样能更快的看清被处理的元素和处理他们的动作。

冗赘的元素

能简单的代码,尽量简单。未来变复杂的时候,再去考虑它。

夸夸其谈的通用性

同上,能简单的代码,尽量简单。通用性?过早的优化是万恶之源

临时字段

临时字段指内部某个字段仅为某种特定情况而设。

临时的字段不应该存在。你需要给他们搬个新家,把所有和临时变量相关的代码搬至那里。

过长的消息链

一长串取值函数,一长串临时变量。

消息链意味着客户端会耦合消息链的查找过程。应该将查找过程独立出一个函数。

中间人

只转发函数,没有自己的功能的就是中间人,应该去掉中间人,直接调用目标函数。

内幕交易

减少模块之间频繁的数据交换,并把这种交换放到明面上。

过大的类

当一个类代码行数太多或者功能职责太多的时候,拆掉它。

两种拆分方法:

  • 提取新类,当大类的部分行为可以分解为一个单独的组件,则可以使用提取类的方式拆分。
  • 提取子类,当大类的部分行为可以以不同的方式实现或在极少数情况下使用,则可以使用提取子类方式拆分。

异曲同工的类

两个类有着相同的功能,但方法名称不同。

提取父类来避免两个类里面有相同的东西

纯数据类

纯数据类没有存在的意义,直接读取对象就可以了。可以将行为搬移的纯数据类里面,这个纯数据类就有存在的意义了。

被拒绝的遗赠

如果子类复用了父类的实现,就应该支持父类的接口。

父类的字段只有一个或少数子类有关 | 将其从父类中挪走,放到真正关心它的子类中去

注释

注释是提示你,这个地方该重构啦。

如果你觉得需要写注释的时候,请先重构,试着让所有注释都变得多余。