代码的坏味道
本书之中的核心之一:简单来说就是碰到什么样子的代码,你就需要警惕起来,需要进行重构了!
本文章中主要分成三部分进行描述,第一部分为名字就是它的术语,第二部分为详解:它的描述及一些实际场景,第三部分重构:就是他的参考重构手法,但这些手法仅作为参考,有时我们可能会需要更多的手法
神秘命名
详解:也包含那些随意的abc或者汉语拼音,总之一切我们看不懂的、烂的都算,好的命名能节省我们很大的时间
重构:改变函数声明、变量改名、字段改名
重复代码
详解:自然这个就好理解了,只要是我们看到两段相似的语法都可以确定为这段代码可以提炼,通常提炼出来会更好,当然这个要看具体情况,个人感觉真的遇到那种只有两处,且代码使用地方八杆子打不着,在代码稳定期间也不用浪费这个时间(这个时间不止体现在改动过程,也包括你可能因为改动导致的隐藏bug,尤其是系统核心模块,一旦出现问题只能马上回滚,不会给你时间去找问题
重构:提炼函数、移动语句、函数上移等手法
过长的函数
描述:短小才是精悍!比如一些条件分支、一个函数做了很多事情、循环内的处理等等的都是应该重构的
重构:提炼函数(常用)、以查询取代临时变量、引入参数对象、保持对象完整性、以命令取代参数(消除一些参数)、分解条件表达式、以多态取代条件表达式(应对分支语句)、拆分循环(应对一个循环做了很多事情)
过长的参数列表
描述:正常来说,函数中所需的东西应该以参数形式传入,避免全局变量的使用,但过长的参数列表其实也很恶心。
重构:查询取代参数、保持对象完整、引入参数对象、移除标记参数、函数组合成类
全局数据
描述:最常见的就是全局变量,但类变量与单例模式也有这样的问题,我们通常无法保证项目启动后不被修改,这就很容易造成诡异的bug,并且很难追查到
重构:封装变量
可变数据
描述:数据的可变性和全局变量一样,如果我其他使用者修改了这个值,而引发不可理喻的bug。 这是很难排查的。
重构:封装变量,拆分变量,移动语句、提炼函数,查询函数和修改函数分离,移除设值函数,以查询取代变量函数组合成类
发散式变化
描述:发散式变化是指某个模块经常因为不同的原因在不同的方向上变化了(可以理解为某一处修改了,造成其他模块方向错乱)
重构:拆分阶段、搬移函数、提炼函数、提炼类
霰弹式修改
描述:和发散式变化接近,却又相反。我们每次修改一个功能或者新增一个功能都需要对多处进行修改;并且随着功能增多我们可能还需要修改更多。 这样程序时是很不健康的,其实我个人理解为:霰弹用来描述发散式变化更好,想想霰弹是一个点发射出去变成很多。而本条应该用另一个词来描述更好,但我还想不到叫什么词。或许叫多路并进?仅限个人观点,每个人理解可能不一样,建议以作者为准
重构:搬移函数、搬移字段、函数组合成类、函数组合成变换、拆分阶段、内联函数、内联字段
依恋情结
描述:一个模块内的一部分频繁的和外面的模块进行交互沟通,甚至超过了它与内部的沟通。也就是违反了高内聚低耦合,遇到这种的“叛乱者”,不如就让他去他想去的地方吧
重构:搬移函数、提炼函数
数据泥团
描述:杂合缠绕在一起的。代码中也如是,我们可能经常看到三四个相同的数据,两个类中相同字段等等。总之像泥一样,这里也是这样那里也是这样,就是他了
重构:提炼类、引入参数对象、保持对象完整性
基本类型偏执
描述:一些基本类型无法表示一个数据的真实意义,例如电话号码、温度等,
重构:以对象取代基本类型、以子类取代类型码、以多态取代条件表达式
重复的switch
描述:不只是switch,大片相联的if也应该包含在内,甚至在古老的前端时代,曾经一度无条件反对这样的写法。
重构:多态取代条件表达式
循环语句
描述:在js中体现为传统的for类循环
重构:用管道来取代循环(管道:map、forEach、reduce、filter等一系列)
冗赘的元素
描述:元素指类和函数,但是这些元素可能因为种种原因,导致函数过于小,导致没有什么作用,以及那些重复的,都可以算作冗赘
重构:内联函数、内联类、折叠继承类
夸夸其谈通用性
描述:为了将来某种需求而实现的某些特殊的处理,但其实可能导致程序难以维护难以理解,直白来说就是没个锤子用的玩意,你留下他干个屁
重构:折叠继承体系、内联函数、内联类、改变函数声明、移除死代码
临时字段
描述:那些本身就足以说明自己是谁的,不需要名字来描述的
重构:提炼类、提炼函数、引入特例
过长的消息链
描述:一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象……这就是消息链,举个例子来说 new Car().properties.bodyWork.material.composition().start() 这意味着在查找过程中,很多的类耦合在一起。个人认为,不仅是结构的耦合,也很难理解。这也包含某类人jq的那一大串的连续调用。都是很难让人理解的。
重构: 隐藏委托关系、提炼函数、搬移函数
中间人
描述:如果一个类有大部分的接口(函数)委托给了同一个调用类。当过度运用这种封装就是一种代码的坏味道
重构:移除中间人、内联函数
内幕交易
描述:两个模块的数据频繁的私下交换数据(可以理解为在程序的不为人知的角落),这样会导致两个模块耦合严重,并且数据交换隐藏在内部,不易被察觉
重构:搬移函数、隐藏委托关系、委托取代子类、委托取代超类
过大的类
描述:单个类做了过多的事情,其内部往往会出现太多字段,一旦如此,重复代码也就接踵而至。这也意味着这个类绝不只是在为一个行为负责
重构:提炼超类、以子类取代类型码
异曲同工的类
描述:两个可以相互替换的类,只有当接口一致才可能被替换
重构:改变函数声明、搬移函数、提炼超类
纯数据类
描述:拥有一些字段以及用于读写的函数,除此之外一无是处的类,一般这样的类往往半一定被其他类频繁的调用(如果是不可修改字段的类,不在此列,不可修改的字段无需封装,直接通过字段取值即可),这样的类往往是我们没有把调用的行为封装进来,将行为封装进来这种情况就能得到很大改善。
重构:封装记录、移除取值函数、搬移函数、提炼函数、拆分阶段
被拒绝的遗赠
描述:这种味道比较奇怪,说的是继承中,子类不想或不需要继承某一些接口,我们可以用函数下移或者字段下移来解决,但不值得每次都这么做,只有当子类复用了超类的行为却又不愿意支持超类的接口时候我们才应该做出重构
重构:委托取代子类、委托取代超类
注释
描述:这里提到注释并非是说注释是一种坏味道,只是有一些人经常将注释当作“除臭剂”来使用(一段很长的代码+一个很长的注释,来帮助解释)。往往遇到这种情况,就意味着:我们需要重构了
重构:提炼函数、改变函数声明、引入断言