第3章 代码的坏味道
3.1 Duplicated Code(重复代码)
如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合而为一,程序会变得更好。
最单纯的重复代码就是“同一个类的两个函数含有相同的表达式”。 Extract Method
另一种常见情况就是“两个互为兄弟的子类内含相同表达式”。 Extract Method,然后Pull Up Method将它推入超类内。如果代码只是相似,并非完全相同,那么就得运用Extract Method将相似部分和差异部分割开,构成单独一个函数。然后你可能发型可以运用Form Template Method获得一个Template Method设计模式。如果有些函数以不同的算法做相同的事,你可以选择其中较清晰的一个,并使用Substitute Algorithm将其他函数的算法替换掉。
如果两个毫不相关的类出现Duplicated Code,你应该对其中一个使用Extract Class。将重复代码提炼到一个独立类中,然后在另一个类内使用这个新类。但是重复代码所在的函数也可能的确只应该属于某个类,另一个类只能调用它,抑或这个函数可能属于第三个类,而另两个类应该引用这第三个类。你必须决定这个函数放在哪儿最合适,并确保它安置后就不会再在其他任何地方出现。
3.2 Long Method(过长函数)
“间接层”所能带来的全部利益——解释能力、共享能力、选择能力——都是由小型函数支持的。
很久以前程序员就已经认识到:程序越长越难理解。早期的编程语言中,子程序调用需要额外开销,这使得人们不乐意使用小函数。现代OO语言几乎已经完全免除了进程内的函数调用开销。
最终的效果就是:你应该更积极地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就要把需要说明的东西写进一个独立函数中,并以其用途(而非实现方法)命名。
百分之九十九的场合里,要把函数变小,只需使用Extract Method。找到函数中适合集中在一起的部分,将它们提炼出来形成一个新函数。
如果函数内有大量参数和临时变量,它们会对你的函数提炼形成阻碍。如果你尝试运用Extract Method,最终就会把许多参数和临时变量当做参数,传递给被提炼出来的新函数,导致可读性几乎没有任何提升。此时,你可以经常使用Replace Temp with Query来消除这些临时元素。Introduce Parameter Object和Preserve Whole Object则可以将过长的参数列变得简洁一些。
如果你这么做了,仍然还有太多临时变量和参数,那就应该使用我们的杀手锏:Replace Method with Method Object.
如何确定该提炼那一段代码呢?一个很好的技巧:寻找注释。它们通常能指出代码用途和实现手法之间的语义距离
条件表达式和循环常常也是提炼的信号。你可以使用Decompose Conditional处理条件表达式。至于循环,你应该将循环和其内的代码提炼到一个独立函数中。
3.3 Large Class(过大的类)
如果想利用单个类做太多事情,其内往往就会出现太多实例变量。
你可以运用Extract Class将几个变量一起提炼至新类内。提炼时应该选择类内彼此相关的变量,将它们放在一起。 如果这个组件适合作为一个子类,你会发现Extract Subclass往往比较简单。
有时候类并非在所有时刻都使用所有实例变量。果真如此,你或许可以多次使用Extract Class或Extract Subclass。
先确定客户端如何使用它们,然后运用Extract Interface为每一种使用方式提炼出一个接口。
如果你的Large Class是个GUI类 Duplicate Observed Data