『重构--改善既有代码的设计』读书笔记----代码坏味道【1】
今天,我们不介绍具体的重构手法。我们暂且缓缓,回过头来看看,总结一下,什么样的代码需要重构,什么样的代码有一股”难闻的味道“,以此来提示我们需要重构,来提示我们这段代码其实是有改进的地方的。我也很喜欢文中作者对于代码这种特质采用”坏味道“,很形象也很生动。下面进入正题:
【1】Duplicated Code(重复代码)
我自己本身也非常讨厌重复代码,似乎每次看到重复代码都标志着其实你这段代码是有问题的,没错,我们确实应该培养这种意识,因为重复代码标志着你以后的改动很可能要同时修改多处地方,你不可能做到完全的统一。人无完人,是人就会犯错误。出现重复代码你就必须要被重复代码扼杀在摇篮里,否则到了之后,你再想去重构,就会发现重复代码变的零零散散,你很难将他们全部一网打尽。如果你将他们合二为一,程序结构会变得更好,你所站的角度会更高,你会看到更多的设计背后的东西。重复代码里面也分几种情况:
1) 在同一个类中的两个函数含有相同的表达式,意味着你可以使用Extract Method,将重复提炼到一个单独的函数中然后让这两个函数去调用它。
2) 两个互为兄弟的子类含有相同的表达式,意味着你可以把他们的公共代码段进行Extract Method然后Pull Up Method推入超类,如果代码只是类似不是完全相同,你可以Extract那些相同的地方,如果你提炼的函数名称在兄弟之间都是相同,你更可以将剩余那些不同的地方设置为虚函数,运用From Template Method形成Template Method设计模式,使结构之间更加完美。如果有些函数以不同的算法做相同的事情,你可以选择其中比较清晰的一个,并使用Substitute Algorithm将这个算法替换掉。
3) 两个毫不相干的类出现了重复代码,你可以考虑使用Extract Class将从重复代码提炼到一个独立类中去,然后在另外一个类中使用这个类就可以了。当然决定这个类是否应该属于第三个类还是说这段代码确确实实属于两个类中的其中一个类,另外一个类来调用他,这个必须由你自己做出决定。
【2】Long Method(过长函数)
过长的函数让别人在看待的时候会觉得不可接受,因为他们复杂,难以重用,稳定度很低。作为面向对象程序元,我们有充分的理由相信短函数的对象能够活得更好,它可以给我们带来更多的便利性。重构长函数无非就是把这些长函数分成一个个短小的小函数。然后他们价值巨大:
1) 他们拥有强大的解释能力,他们自身的函数名称就代表了他们本身这个函数所要表达的东西,这是告诉你做什么,而不是给你看他怎么做。注意我的用词,”做什么“和“怎么做”距离巨大。我们平常阅读程序更多的应该去看程序做什么,让我们了解到这段程序具体是做什么,而不是我们要去看这段程序是怎么做的。有些时候面对别人写的程序或者接口我们根本没有义务也没必要去查看他是怎么做到的。
2) 他们拥有共享能力。短小函数与长函数的区别就是复用性,短小函数可以更好的被我们复用,而长函数往往因为场合因素,牵扯的变量太多往往导致难以复用。
3) 他们拥有选择能力。鉴于共享,让他们彼此之间可以更好的选择,客户在使用函数的时候可以有更高的灵活性。
把长函数重构成短小的难题在于起一个好名字,是的,你没有看错,一个函数的名称对函数来说至关重要。越是经验资历比较老的程序员在函数命名的选择上越加谨慎。其实我们可以去关注他们的命名方式,确实十分严谨,切好实际语义。而那些经验比较少的程序员,年轻程序员在函数以及变量命名上就十分草率,int i, j, k的时代已经过去了。现在我们需要让他们有能够解释自身的能力。
重构手法应该用Extract Method就已经足够了,如果临时变量成为你重构的阻碍,你可以尝试使用Replace Temp with Query来消灭这些临时变量。如果你提炼出来的函数的参数过多,你也可以尝试使用Introduce Parameter Object和Preserve Whole Object来缩短你的参数列表。当然还有最后一招杀手锏,如果你仍有有大量的临时变量和参数的话,你就应该用Replace Method with Method Object。
时间点也很重要,这里也有一个小窍门,如果你感觉某一段代码特别需要一句注释来说明你这段代码的用意,那么,你的机会来了,其实这就表示你的代码实现和代码语义实际上出现了比较大的距离。你需要用注释来修正。我们前面也讲到了Extract Method的用途之一就是用来缩短实现和语义的距离。因此,大胆放心去重构吧。当然条件表达式和循环常常也是提炼的信号,前者你可以使用Decompose Conditional来重构,后者你可以将循环和其内的代码进行Extract Method到一个独立函数中去。
今天就先讲这么多,因为这段时间我推出的重构手法中,有很多朋友私信跟我联系重构的意义和必要性,跟我一起探讨重构之后程序的整洁度与清晰度与重构之前的距离。我很感谢这些朋友能够与我互动,让我有持续写下去的毅力和勇气。所以今天暂且不出重构手法,我们稍做整理,来闻一下“坏味道”我觉得更好:)