作为程序员,每天要做的不单单是面向对象编程,同时也要面向未来编程。代码重构永远是程序员们无法回避的话题,当你的软件在编写的那一刻起,重构就不可避免。编写一个项目系统,我们为什么要费劲地不断抽象,竭尽全力让自己的代码能够被重用,说白了就是让我们今日所付出的时间,让未来的我们能够更轻松地工作而已。
这里我谈谈自己在工作中的一些积累和经验,以及对代码重构的看法
自动化测试
重构代码有一个很重要的先置条件:自动化测试。
写代码的目的是什么?是为了产出的系统能够满足功能需求。
重构代码的目的是什么?是为了满足功能需求的代码质量和效率能够更优质,让未来的工作更轻松,以及让代码的可读性更友好。
那我们需要考虑到的是,如果重构了代码,却破坏了基本的功能,纵使代码再漂亮,性能再高,又有何用?
因此,为了保证重构不破坏既有的功能,必须引入自动化测试。无论是单元测试,功能测试,集成测试都囊括,总之需要尽一切可能去测试。相关的测试是否存在决定了你能否重构;而测试所花费的时间直接决定了你是否会进行重构,以及以一个什么样的频率进行重构。如果重构了十行代码,却需要花费一个小时进行运行一次单元测试,那么你要么不会去重构代码,要么你重构了不会去测试。
好的重构发生在构建系统的每时每刻,而非问题发生或者老板要求。如果重构之后测试立刻会告知你结果,你会更有信心进行更多的重构,使其成为你工作生活的一部分。
何时重构
前面已经提到了重构代码最佳的时间点:撰写每行代码的时候,而非火烧屁股的时候。那什么样的情况你需要进行重构呢?我的意见是,当前下面这些让你感到不太舒服的场景出现,其实是代码在提醒你:“我该被重构了。”
1. 当你写一段代码时,不得不从别处拷贝粘贴代码
一段代码(文档,测试,注释)如果要被复制,那么它的逻辑就该被抽取出来,单独成文。这几乎是重构最基础的实践。然而,这个问题,从小公司到大公司,几乎是每个系统最严重的问题之一。在我以前工作的公司,我维护过一个超过 5000 行的 C 函数,里面的 if-else 层层嵌套下的 copy&paste 让人叹为观止,添加一点逻辑需要检查七八个地方是否需要同样的逻辑,完全可以入选教材作为经典的反面案例。
2. 当你修改已有代码添加新功能时,发现已有代码总感觉哪里不对
比如说,逻辑写得太绕,太复杂,太难以理解,循环太多,分支太多,状态太多等等。这样的代码几乎跪在那里请求你的重构,不重构确实说不过去。
3. 当你调用已有的代码时(函数,类),必须阅读被调用的代码才能确定如何调用
发生这种情况,要么是代码的接口定义的不好,比如说,一个函数有十多个参数;要么是文档写的不好,比如说,关键性的函数没有对接口提供足够的说明。如果说上面所述的是纯粹的代码重构,那么这里就是用户体验的重构。程序员的代码是什么?那是一个程序员为另一个程序员精心打造的产品。
4. 当你写一段代码时,连带着要改动多处关联代码
当这个场景发生的时候,代码的味道相当糟糕,意味着不仅代码本身有问题,相关代码的设计甚至架构也有很大的问题。如果没有一定功底的程序员,重构这样的代码会比较费劲。
培养自律的精神
稍微大一点的软件项目是多人一起合作完成的。和别人合作,我们要坚信两点:人天性都是懒惰的,有捷径的话,绝不规规矩矩走大道;同时人都会受到 role model 或者社区的感染,如果已有的代码库形成了一个良好的氛围,新加入的人有一种融入已有体系的紧迫感。开源项目其实可以给我们很多启发,看看那些著名的开源项目,很多参与其中的人在他们各自的公司里都未必有这么好的习惯,但在开源项目中,项目本身的检测和社区带来的压力会让它们自律。
对于人的这两种天性,我们需要掌握方法进行合理引导,下面举一个例子。
除了上述的基本要求外,我一般还要根据实际需要添加这些要求:
一个函数最多有 50 行代码。那么超过 50 行代码怎么办?要么拆分之,要么精简之。
一个函数的嵌套不能超过 5 层。多个 for 循环,深层的 if-else,这些都是罪恶之源。如果超过这个限制,只能拆分,或者使用函数式编程:map/filter/reduce。
一个函数最多有 3 层 callback。这是逼着程序员不要误用 callback,尽量多用 Promise。
一个函数最多 5 个参数。参数太多的函数,基本是试图揉太多事情在一起。
一个函数的复杂性不超过 10。你的所有分支,循环,回调等等统统加在一起,在一个函数里不超过 10 个(注意不是嵌套)。
接下来还要有Role Model,或者整体氛围来引导行为。一般我启动的项目,我会撰写初始的项目,力保每行代码清晰可读,每个函数深思熟虑,每个接口都有友好的文档,每个关键的函数有详尽的测试,然后在Code review环节严格把关。这样,在更多的人加入项目后,大家前有模板可循,经验可依,后有鞭策之威,自然写出比较漂亮的代码。
整个过程看上去和代码重构看上去没什么关系,但处处要求程序员重构代码以达到比较高的标准。相信我,这么做即便大家开始不适,等渐渐建立信心之后,对自我有要求的程序猿就会开始强迫症发作,开始追寻更高质量的代码。