章节三 代码的坏味道
*这一部分的内容最好在CHM文件中去阅读
Duplicated Code (重复代码)
同一个class内的两个函数含有相同表达式(expression)
采用Extract Method提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。
两个互为兄弟〔sibling)的subclasses内含相同表达式
对两个classes都使用Extract Method,然后再对被提炼出来的代码使用 Pull Up Field,将它推入superclass内。如果代码之 间只是类似,并非完全相同,那么就得运用Extract Method将相似部分和差异部分割开,构成单独一个函数。然后你可能发现或许可以运用Form Template Method获得一个Template Method设计模式。如果有些函数以不同的算法做相同的事,你可以择定其中较清晰的一个,并使用Substitute Algorithm将其他函数的算法替换掉。
两个毫不相关的classes内出现Duplicated Code
应该考虑对其中一个使用Extract Class,将重复代码提炼到一个独立class中,然后在另一个class内 使用这个新class。但是,重复代码所在的函数也可能的确只应该属于某个class, 另一个class只能调用它,抑或这个函数可能属于第三个class,而另两个classes应该引用这第三个class。你必须决定这个函数放在哪儿最合适,并确保它被安置后就不会再在其他任何地方出现。
Long Method(过长函数)
让small method容易理解的真正关键在于一个好名字。如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。
遵循这样一条原则:==每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。== 我们可以对一组或甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数「做什么」和「如何做」之间的语义距离。
Large Class(过大类)
可以运用Extract Class将数个变量一起提炼至新class内。提炼时应该选择class内彼此相关的变量,将它们放在一起。
Long Parameter List(过长参数列)
如果「向既有对象发出一条请求」就可以取得原本位于参数列上的一份数据,那么 你应该激活重构准则Replace Parameter with Method 。上述的既有对象可能是函数所属class内的一个值域(field),也可能是另一个参数。你还可以运用Preserve Whole Object 将来自同一对象的一堆数据收集起来,并以该对象替换它们。如果某些数据缺乏合理的对象归属,可使用Introduce Parameter Object 为它们制造出一个「参数对象」。
Divergent Change(发散式变化)
如果某个class经常因为不同的原因在不同的方向上发生变化,Divergent Change就出现了。当你看着一个class说:『呃,如果新加入一个数据库,我必须修改这三个函数;如果新出现一种金融工具,我必须修改这四个函数』,那么此时也许将这个对象分成两个会更好,这么一来每个对象就可以只因一种变化而需要修改。当然,往往只有在加入新数据库或新金融工具后,你才能发现这一点。==针对某一外界 变化的所有相应修改,都只应该发生在单一class中,== 而这个新class内的所有内容都应该反应该外界变化。为此,你应该找出因着某特定原因而造成的所有变化,然后运用Extract Class 将它们提炼到另一个class中。
Shotgun Surgery(散弹式修改)
Shotgun Surgery类似Divergent Change,但恰恰相反。如果每遇到某种变化,你都必须在许多不同的classes内做出许多小修改以响应之,你所面临的坏味道就是Shotgun Surgery。如果需要修改的代码散布四处,你不但很难找到它们,也很容易忘记某个重要的修改。
这种情况下你应该使用Move Method 和 Move Field 把所有需要修改的代码放进同一个class。如果眼下没有合适的可以安置这些代码,就创造一 个。通常你可以运用Inline Class 把一系列相关行为放进同一个class。这可能会造成少量Divergent Change,但你可以轻易处理它。<