代码的坏味道

Duplicated Code(重复的代码)

坏味道行列中首当其冲的就是Duplicated Code。如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合而为一,程序会变得更好。

最单纯的Duplicated Code就是“同一个类的两个函数含有相同的表达式”。这时候你需要做的就是采用Extract Method提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。

另一种常见的情况就是“两个互为兄弟的子类内含相同的表达式”。要避免这种情况,只需对两个类都是用Extract Method,然后再对被提炼出来的代码使用Pull Up Method,将它推入超类内。如果代码之间只是类似,并非完全相同,那么就运用Extract Method将相似部分和差异部分割开,构成单独一个函数。然后你可能发现可以运用Form Template Method获得一个Templata Method设计模式。如果有些函数以不同的算法做相同的事,你可以选择其中较清晰的一个,并使用Subsitute Algorithm将其他函数的算法替换掉。

如果两个毫不相关的类出现Duplicated Code,你应该考虑对其中一个使用Extract Class,将重复代码提炼到一个独立类中,然后在另一个类内使用这个新类。但是,重复代码所在函数也可能的确只应该属于某个类,另一个类只能调用它,抑或这个函数可能属于第三个类,而另两个类应该引用这第三个类。你必须决定这个函数放在哪儿最合适,并确保它被安置后就不会再在其他任何地方出现。

Long Method(过长函数)

拥有短函数的对象会活得比较好、比较长。不熟悉面向对象技术的人,常常觉得对象程序中只有无穷无尽的委托,根本没有进行任何计算。和此类程序共同生活数年之后,你才会知道,这些小小函数有多大价值。“间接层”所能带来的全部利益——解释能力、共享能力、选择能力——都是由小型函数支持的。

很久以前程序员就已经认识到:程序越长越难理解。早期的编程语言中,子程序调用需要额外开销,这使得人们不太乐意使用小函数。现在OO语言几乎已经完全免除了进程内的函数调用开销。不过代码阅读者还是得多费力气,因为他必须经常转换上下文去看看子程序做了什么。某些开发环境允许用户同时看到两个函数,这可以帮助你省去部分麻烦,但是让小函数容易理解的真正关键在于一个好名字。如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。

最终的效果是:你应该更积极地分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。我们可以对一组甚至短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地那么做。关键不在于函数的长度,而在于函数“做什么”和“如何做”之间的语义距离。

百分之九十九的场合里,要把函数变小,只需使用Extract Method。找到函数中适合集中在一起的部分,将它们提炼出来形成一个新函数。

如果函数内有大量的参数和临时变量,它们会对你的函数提炼形成阻碍。如果你尝试运用Extract Method,最终就会把许多参数和临时变量当作参数,传递给被提炼出来的新函数,导致可读性几乎没有任何提升。此时,你可以经常运用Replace Temp with Query来消除这些临时元素。Introduce Parameter Object和Preserve Whole Object则可以将过长的参数列变得更简洁一些。

如果你已经这么做了,仍然有太多临时变量和参数,那就应该使出我们的杀手锏:Replace Method with Method Object

如何确定该提炼哪一段代码呢?一个很好的技巧是:寻找注释。它们通常能指出代码用途和实现手法之间的语义距离。如果代码前方有一行注释,就是在提醒你:可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数去。

条件表达式和循环常常也是提炼的信号。你可以使用Decompose Conditional处理条件表达式。至于循环,你可以将循环和其内的代码提炼到一个独立函数中。

Large Class(过大的类)

如果想利用单个类做太多事情,其内往往就会出现太多实例变量。一旦如此,Duplicated Code也就接踵而至了。

你可以运用Extract Class将几个变量一起提炼至新类内。提炼时应该选择类内彼此相关的变量,将它们放在一起。例如,depositAmount和depositCurrency可能应该隶属同一个类。通常如果类内得数个变量有着相同得前缀或字尾,这就意味有机会把它们提炼到某个组件内。如果这个组件适合作为一个子类,你会发现Extract Subclass往往比较简单。

有时候类并非在所有时刻都使用所有实例变量。果真如此,你或许可以多次使用Extract Class或Extract Subclass。

和“太多实例变量”一样,类内如果有太多代码,也是代码重复、混乱并最终走向死亡得源头。最简单得解决方案(还记得吗,我们喜欢简单得解决方案)是把

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值