关于代码质量的一些思考
今天刚好看到同事写一段代码,跟同事聊到一个代码风格的问题,讨论了一会,也没得出什么结果。回来想了想,之所以大家观点不一样,其实是一开始代码追求的目的就不一样。
1. 可读性
我是一直认为代码的可读性是最重要的目标。太多的书都讲到一个观点:“代码是写给人阅读的,只不过刚好能被计算机执行”。
大部分做自己产品的团队,一个项目的维护时间可能是开发时间的5倍以上,而维护的常见内容都是一些小功能以及已有bug的修复。可读性带来的好处就是,非常容易弄清一段功能逻辑,从而定位问题。遇到团队人员变动,新人也能很快的熟悉。我在公司换过很多组,也接手过很多的项目(大多数的可读性并不好),就这一点来说,真是切肤之痛。
什么样的代码,算是可读性好?我跟别人提过一个标准:“你写的代码,过了几个月、半年、一年,跟你说道一个功能,即使你不记得这个功能怎么做,你也能说清楚这个功能写在哪个地方”。这个标准我自己认为还是很有效的。
那用什么方法可以增加可读性呢?合理的拆分和抽象会增加可读性。另外,我其实一直崇尚“用最常用的方法写程序,直到它发展到你已经理解困难的时候,再去重构”。
2. 代码美学与合理性
经历过跟很多人的合作,我发现,很多非常优秀的开发者,会从直觉上把一个代码片段“是否优美”作为第一考虑的目标。他们会追求一些高级编程技巧的合理运用,或者开发一些公共组件,来达到行数足够少,或是表达足够清晰的目标。这个好像教材里也不曾提到的,我把它叫做“合理的代码美学”。
对于Java代码来说,有人就喜欢把一些常见功能,用自定义注解,然后用AOP来完成注解的解释。例如,一个功能需要随时可以打开或关闭,我可以通过一个注解来完成它而不是在业务处理中写一些if-else-check。
@NewFeatureEnabled
public void doSomething(){
}
实际上,完成一个“优美的”小功能,也就是用自己心中认为最好的方法去完成一件事,这样的满足感,也是让人持续编程的动力。严格的说,这样的代码有没有给代码质量带来提升呢?肯定有。第一很多时候这样的代码会经过更多的考虑,必然有更高的质量;第二很多更好的开发技巧,都是来自这些“不一样”的追求。
但是我认为,软件开发与艺术最大的不同是,它是一个多人合作劳动,一个人觉得合理的,其他人未必会感同身受,甚至会恰恰相反。这样的代码是蛋糕上华丽的三层奶油,有时会给人眼前一亮的感觉,但是也可能会让人找不到蛋糕本身。所以我的建议是:“如果你要用一个新技巧,最好积极宣传,和团队达成共识”。
比如这个@NewFeatureEnabled
的功能,我会想:“如果别人接手这个项目,他是不是知道NewFeatureEnabled是什么意思?即使知道,他怎么知道这个功能的开关,是由其他地方一个AOP来完成的呢?”但是如果大家都接受这个方式,知道AOP是在哪里配置,如何工作的,那么也就是一个还不错的尝试了。
3. 可复用性
追求复用性是开发者的一个本能。大家都希望少写代码,最好要用的时候,一切都准备好了。我见过太多的代码为了复用而设计,但是基本上所有以复用为第一目标的代码,都没有什么好质量。不是说可复用性不重要,而是它容易让人走上歪路。
比较好的复用方式,应该是零件式的复用,每一块都有各自的规范和存在,但是这样的复用是一个严肃的过程,往往也达不到最大化的“代码复用”。为了复用的抽象,常见的就是把一段公用的代码块独立出来成为函数或者类,而这部分的逻辑甚至都是无法独立存在,也单独无法被人理解的。这种拼接式的复用,类似于为了表示一只猫和一只狗,把猫的身体和狗的身体复用成一块,然后写一堆判断代码来告诉别人什么时候这个身体是猫,什么时候这个身体是狗,最后再跟各自的脑袋组合起来。当然了,没有人知道这个“既是猫又是狗的身体”是什么。如果你想知道猫长什么样,估计得先给它做个缝合手术才行。
如果一个项目以这样的思路开发久了,你会发现代码逻辑散落在各处,各种业务场景互相交织,任何改动都会牵一发动全身。
如果要为可读性排个名的话,我认为大概是:
毫无头绪的抽象<只为复用的抽象<不做抽象<简单的抽象<局部优美的代码<整体优美的代码<清晰的层次结构并抽象<整体优美并且被大家接受的代码
最后一个层级,这样的代码实际上已经是更高的生产力了,就像Spring之于满地new对象就是个进步。可能代码进化到最后,真的就是跟自然语言那么简单,到时候我们就需要研究怎么在代码里写诗了!