在编程时,注释一般都会被认为是好习惯,可以增加代码的可读性,但实际上注释也是有缺点的,而且怎么合理地使用注释也是有讲究的,这里讨论一下关于注释的问题。
先说注释的缺点,注释跟代码其实是一种依赖关系,所谓依赖关系,就是当一个产生变化的时候,另一个也需要相应变化,不然的话就会有不对应的问题。直白点讲,就是当代码有改动时,注释可能就不再正确了,需要作相对应的修改。在项目里,尤其是处于开发阶段的项目,功能变动和代码变动是常有的事情,如果在一个相对不稳定的环境下添加了不少注释,以后碰到代码变动,注释修改起来也是要额外的成本的。有时候程序员把代码改了,但注释没改(改代码的人也可能不是刚开始写程序的人),这样日积月累,注释就很可能就跟代码脱节了,后面接手的人去注释理解功能反而可能会被误导。注释还有个问题是内容太多的话会影响代码的阅读,我曾经看到过一个为API自动生成注释的功能,会给API在头部加上命名,描述,参数类型,调用例子等各种注释,导致原来一个屏幕可以看好几个API的,现在光一个API和它的注释就占了大半屏幕,这其实算是一种“视觉垃圾”,如果把程序员当成是用户的话,这种是不符合UI设计的,更好一点的做法是做成链接或者悬浮提示(这里只说设计,不说技术上能否实现)。
回到标题,“最好的注释就是代码”,因为注释往往是需要额外的成本,也可能是不靠谱的,所以保证代码可读性最重要的就是把代码本身,有些人可能会想到文档,但跟着代码的注释都不一定靠谱,更别说文档了。实际上在做过很多维护之后,想了解一些原先的业务逻辑,除了正好有懂这块的业务顾问或者其他什么人,你能依赖的只有代码,很多时候需要靠阅读代码才能倒推业务逻辑,而不是靠注释和文档了解业务。这里说一下我写代码的几个原则:
一,代码要尽量接近自然语言,从视觉角度看上去,要更像是一篇文章,而不是一堆符号的集合。注意,ABAP起初也是基于这个原则而开发的,但在发展过程中已经越来越脱离这个原则了。这一点是不容易做到的,因为接近自然语言的话,在写的时候会多码很多字,而用简单的符号表示,写起来更省事,但是我相信在读代码的时候,你肯定更加愿意看到表达更加清晰的文字,而不是一堆符号,这个优点时间越久体现地越明显。
二,“所见即所得”,业务逻辑相关的代码,跟业务本身应该是一一对应的关系。代码要写得更加符合人的思维,让人一看就明白是什么意思。举个简单的例子,某种订单有A和B两种类型,业务需求是当类型是A的时候,去执行下一步操作。那代码就应该写成if(类型等于A),但可能有人会写成if(类型不等于B),这就不符合“所见即所得”,当你看这样代码的时候,还需要多思考一步“不等于B,那就是A”,由于这个例子非常简单,所以不会造成比较大的理解困难,但实际业务场景有些逻辑判断条件是很复杂的,有可能涉及到十来个条件,互相之间还有依赖,如果不是严格地按照业务需求去写程序逻辑,一方面容易考虑不全出漏洞,一方面也为后来的维护造成了困难,到时候别人看代码将会很吃力,哪怕是你自己维护的,也可能一时半会儿理解不了当时为什么会那样写。
尽量写好代码,是代码可读性的第一因素,但有时候注释还是免不了的,关于怎么样写注释我有一些自己的见解:
一,尽量针对功能写注释,而不要针对程序代码写。比如这一块功能是取XX数据,那么就把这个信息写在注释里,但具体怎么取数的过程,比如什么表查询什么字段,就可以不用写,因为数据的取法是可能变化的,也许之后会被要求用一个标准的function取数,那之前的注释就都白写了。功能虽然也可能变,但相对具体实现代码来说还是更加稳定的。
二,写注释要注意注释的时效性,有些人当他修改了一个什么东西后,会把关于这个修改的的编号(可能是项目文档编号,也可能task编号)写在注释里,然后再把当时的年月日写上去。这里有个问题是,系统或者说程序,跟项目文档不是一一对应的关系,比如说如果某个功能一直有问题,建了好几个相关的task去修复,那这几个修复都是针对同一个功能的,显然没有必要把每次怎么修复的都写在注释了。注释注重的是“这段程序是什么”,而不是“我干了什么”,注释是描述系统功能的,而不是用来记录开发历程的。每次的修复都会随着时间的推移变得不再重要,重要的是当前是什么。回到一开始说的,把修改的日期写在注释里,从一定程度上可以缓解注释过期的问题,但是同时在时间长了以后,这些注释都可能会变成无用信息(也许它依然正确,但是你并不关心当时发生了什么),而对阅读代码造成干扰。