Litong's Blog

Work to become, not to acquire.

第3章 代码的坏味道

如果尿布臭了,就换掉它。

决定合适重构及合适停止和知道重构机制如何运转一样重要。

神秘命名

整洁代码最重要的一环就是好的名字,命名是编程中最难的两件事情之一。

好名字的背后是更深层次的设计问题。

重复代码

重组代码顺序、提炼函数、上移函数。

过长函数

活得最长、最好的程序,其中的函数都比较短。

间接性带来的好处——更好的阐释力、更易于分享、更多的选择——都是由小函数来支持的。

函数越长,就越难理解。

让小函数易于理解的关键还是在于好名字,通过名字了解函数的作用,根本不必去看其中写了些什么。

每当感觉需要以注释来说明点什么的时候,就应该积极地分解函数。

关键不在于函数的长度,而在于函数”做什么“和”如何做“之间的语义距离。

提炼函数的着手点:寻找注释(语义距离)、条件表达式、循环(拆分循环和循环内的代码、拆分循环为各自独立的任务)。

过长参数列表

使用类可以有效地缩短参数列表。

全局数据

封装全局变量,控制其作用域。

良药与毒药的区别在于剂量。

可变数据

对数据的修改经常导致出乎意料的结果和难以发现的bug。

封装变量、拆分变量、移动语句、提炼函数、修改函数分离、移除Setter函数。

如果变量可以被计算出来,请使用查询取代派生变量。

控制作用域。

发散式变化

如果某个模块经常因为不同的原因在不同的方向上发生变化,发散式变化就出现了。

每次只关心一个上下文。

如果发生变化的两个方向自然地形成了先后次序,就可以用拆分阶段将其分开,两者之间通过一个清晰的数据结构进行沟通。

如果两个方向之间有更多的来回调用,就应该先创建适当的模块,然后用搬移函数把处理逻辑分开。

如果函数内部混合了两类处理逻辑,先用提炼函数,再做搬移。

散弹式修改

如果每遇到某种变化,你都必须在许多不同的类内做出许多小修改,散弹式修改就出现了。

可以使用搬移函数、函数组合成类、函数组合成变换、拆分阶段、内联函数。

依恋情节

所谓模块化,就是力求将代码分出区域,最大化区域内部的交互、最小化跨区域的交互。

如果一个函数总是或大部分情况调用另一个函数,那么就应该使用搬移函数将其移过去。

如果只是函数中的一部分有这种函数调用的依恋情况,那就应该使用提炼函数,再搬移函数。

将总是一起变化的东西放在一块儿,比如策略模式、访问者模式。

数据泥团

数据项就像小孩,喜欢三五成群的出现,运用提炼类提炼到一个独立对象中。

基本类型偏执

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

字符串是这种坏味道的最佳培养皿。

以对象取代基本类型、以子类取代类型码+多态取代条件表达式。

重复的switch

用多态代替重复的switch语句

循环语句

用管道取代循环语句

冗赘的元素

使用内联函数、内联类。

夸夸其谈通用性

不要提前做”聪明的决定“,请等待到有足够信息的那时候才决定。

临时字段

如果类中的某个字段仅为某种特定情况而设,那么就应当移除它。

过长的消息链

使用隐藏委托关系,在消息链的不同位置采用这种重构手法。

中间人

封装往往伴随着委托,如果过度使用就应该移除中间人,直接和真正负责的对象打交道。

内幕交易

模块之间一定的数据交换不可避免,但我们必须尽量减少这种情况,并把这种交换都放到明面上来。

好的设计是不试图隐藏什么。或者说好的设计是能明确地表达出系统的意图。

继承常会造成密谋,因为子类对超累的了解总是超过后者的主观愿望。以委托取代子类、以委托取代超类。

过大的类

提炼为更小的类。

异曲同工的类

搬移函数、提炼函数,减少重复。

纯数据类

不可修改的字段无须封装。

被拒绝的馈赠

如果子类不想或无需继承超类的函数和数据,那么就说明继承体系设计错误。

注释

如果需要注释来解释代码的意图,通常意味着可以提炼函数了。

如果你不知道该做什么,这才是注释的良好运用时机。