[2] Long Method (过长函数)
2 Long Method (过长函数)
拥有短函数的对象会活的比较好,比较长. 不熟悉面向对象的人,常常觉得对象程序中只有无穷无尽的委托,根本没有进行任何计算. 和此类程序共同生活数年之后,你才会知道, 这些小小函数有多大价值. "间接层"所能带来的全部利益- 解释能力,共享能力,选择能力.这都是由小型函数支持的.
很久以前程序员就已经认识到: 程序越长越难理解. 早期的程序语言中, 子程序调用需要额外的开销, 这使得人们不太乐意使用小函数. 现代OO语言几乎已经完全免除了进程内调用开销. 不过代码阅读者还是得多费力气. 因为他必须常转换上下文去看看子程序做了什么. 某些开发环境允许用户同时看到两个函数. 这可以帮助你省去部分麻烦. 但是让小函数容易理解的真正关键在于一个好名字. 如果你能给函数起个好名字. 读者就可以通过名字了解函数的作用了, 根本不必去看其中写了些什么.
最终的结果是: 你应该更积极地分解函数. 我们遵循这样一个原则: 每当感觉需要以注释说明点什么的时候. 我们就把需要说明的东西写进一个独立的函数中, 并以其用途(而非实现手法)命名. 我们可以对一组甚至短短一行代码做这件事.哪怕替换后的函数调用动作比函数自身还长, 只要函数名称能够解释其用途, 我们也该毫不犹豫地那么做. 关键不在于函数的长度, 而在于函数"做什么" 和 "如何做" 之间的语意距离.
99%的场合中, 要把函数变小, 需要使用 Extract Method(提炼函数), 找到函数中适合集中在一起的部分, 将他们提炼出来形成一个新函数.
如果函数内有大量的参数和临时变量, 他们会对你的函数提炼形成阻碍. 如果你尝试使用Extract Mothod(提炼函数), 最终就会把许多参数和临时变量当作参数, 传递给被提炼出来的新函数, 导致可读性几乎没有任何提升. 此时, 你可以经常运用Replace Temp With Query(以查询取代临时变量)[这个方法往往是你运用Extract Method之前比不可少的一个步骤] 来消除这些临时元素. Introduce Parameter Object(引入参数对象) 和 Preserve Whole Object(保持对象完整)则可以讲过长的参数列变得更简洁一些.
如果你已经这么做了, 仍然有太多临时变量和参数, 那就应该使出我们的杀手锏: Replace Method With Method Object(以函数对象取代函数).
如何确定该提炼那一段代码呢? 一个很好的技巧是: 寻找注释. 他们通常能指出代码用途和实现手法之间的语义距离. 如果代码前方有一行注释, 就是提醒你: 可以讲这段代码替换成一个函数, 而且可以在注释的基础上给这个函数命名. 就算只有一行代码, 如果它需要以注释来说明. 那也值得将它提炼到独立函数去.
条件表达式和循环常常也是提炼的信号. 你可以使用Decompose Conditional(分解条件表达式) 处理条件表达式. 至于循环, 你应该讲循环和其内的代码提炼到一个独立函数中.