第3章 代码的坏味道
如果尿布臭了,就换掉它。
决定合适重构及合适停止和知道重构机制如何运转一样重要。
神秘命名
整洁代码最重要的一环就是好的名字,命名是编程中最难的两件事情之一。
好名字的背后是更深层次的设计问题。
重复代码
重组代码顺序、提炼函数、上移函数。
过长函数
活得最长、最好的程序,其中的函数都比较短。
间接性带来的好处——更好的阐释力、更易于分享、更多的选择——都是由小函数来支持的。
函数越长,就越难理解。
让小函数易于理解的关键还是在于好名字,通过名字了解函数的作用,根本不必去看其中写了些什么。
每当感觉需要以注释来说明点什么的时候,就应该积极地分解函数。
关键不在于函数的长度,而在于函数”做什么“和”如何做“之间的语义距离。
提炼函数的着手点:寻找注释(语义距离)、条件表达式、循环(拆分循环和循环内的代码、拆分循环为各自独立的任务)。
过长参数列表
使用类可以有效地缩短参数列表。
全局数据
封装全局变量,控制其作用域。
良药与毒药的区别在于剂量。
可变数据
对数据的修改经常导致出乎意料的结果和难以发现的bug。
封装变量、拆分变量、移动语句、提炼函数、修改函数分离、移除Setter函数。
如果变量可以被计算出来,请使用查询取代派生变量。
控制作用域。
发散式变化
如果某个模块经常因为不同的原因在不同的方向上发生变化,发散式变化就出现了。
每次只关心一个上下文。
如果发生变化的两个方向自然地形成了先后次序,就可以用拆分阶段将其分开,两者之间通过一个清晰的数据结构进行沟通。
如果两个方向之间有更多的来回调用,就应该先创建适当的模块,然后用搬移函数把处理逻辑分开。
如果函数内部混合了两类处理逻辑,先用提炼函数,再做搬移。
散弹式修改
如果每遇到某种变化,你都必须在许多不同的类内做出许多小修改,散弹式修改就出现了。
可以使用搬移函数、函数组合成类、函数组合成变换、拆分阶段、内联函数。
依恋情节
所谓模块化,就是力求将代码分出区域,最大化区域内部的交互、最小化跨区域的交互。
如果一个函数总是或大部分情况调用另一个函数,那么就应该使用搬移函数将其移过去。
如果只是函数中的一部分有这种函数调用的依恋情况,那就应该使用提炼函数,再搬移函数。
将总是一起变化的东西放在一块儿,比如策略模式、访问者模式。
数据泥团
数据项就像小孩,喜欢三五成群的出现,运用提炼类提炼到一个独立对象中。
基本类型偏执
许多程序员不愿意创建对自己的问题域有用的基本类型,如钱、坐标、范围等。
字符串是这种坏味道的最佳培养皿。
以对象取代基本类型、以子类取代类型码+多态取代条件表达式。
重复的switch
用多态代替重复的switch语句
循环语句
用管道取代循环语句
冗赘的元素
使用内联函数、内联类。
夸夸其谈通用性
不要提前做”聪明的决定“,请等待到有足够信息的那时候才决定。
临时字段
如果类中的某个字段仅为某种特定情况而设,那么就应当移除它。
过长的消息链
使用隐藏委托关系,在消息链的不同位置采用这种重构手法。
中间人
封装往往伴随着委托,如果过度使用就应该移除中间人,直接和真正负责的对象打交道。
内幕交易
模块之间一定的数据交换不可避免,但我们必须尽量减少这种情况,并把这种交换都放到明面上来。
好的设计是不试图隐藏什么。或者说好的设计是能明确地表达出系统的意图。
继承常会造成密谋,因为子类对超累的了解总是超过后者的主观愿望。以委托取代子类、以委托取代超类。
过大的类
提炼为更小的类。
异曲同工的类
搬移函数、提炼函数,减少重复。
纯数据类
不可修改的字段无须封装。
被拒绝的馈赠
如果子类不想或无需继承超类的函数和数据,那么就说明继承体系设计错误。
注释
如果需要注释来解释代码的意图,通常意味着可以提炼函数了。
如果你不知道该做什么,这才是注释的良好运用时机。