第四章 注释
概述
什么也比不上放置良好的注释来得有用。什么也不会比乱七八糟的注释更有本事搞乱模块。什么也不会比陈旧、提供错误信息的注释更有破坏性。
注释并不像辛德勒的名单。它们并不“纯然地好”。实际上,注释最多也就是一种必须的恶。若编程语言足够有表达力,或者我们长于用这些语言来表达意图,就不那么需要注释——也许根本不需要。
注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败。
注释存在的时间越久,就离其所描述的代码越远,越来越变得全然错误。原因很简单。程序员不能坚持维护注释。
不准确的注释要比没注释坏得多。它们满口胡言。它们预期的东西永不能实现。它们设定了无需也不应再遵循的旧规真实只在一处地方有:代码。只有代码能忠实地告诉你它做的事。那是唯一真正准确信息来源。所以,尽管有时也需要注释,我们也该多花心思尽量减少注释量。
1. 好的注释
有些注释是必须的,也是有利的。来看看一些我认为值得写的注释。不过要记住,唯真正好的注释是你想办法不去写的注释。
1.1 法律信息
有时,公司代码规范要求编写与法律有关的注释。例如,版权及著作权声明就是必须和有理由在每个源文件开头注释处放置的内容。
eg:
1.2 提供信息的注释
有时,用注释来提供基本信息也有其用处。例如,以下注释解释了某个抽象方法的返回值:
这类注释有时管用,但更好的方式是尽量利用函数名称传达信息。
1.3 对意图的解释
有时,注释不仅提供了有关实现的有用信息,而且还提供了某个决定后面的意图。这类进场出现在方法中,表示我这一步代码的意图到底是什么。
eg:
public void test(){
...
for(Student student:studentList){
//为学生设置学号
....
}
...
}
1.4 阐释
有时,注释把某些晦涩难明的参数或返回值的意义翻译为某种可读形式,也会是有用的。
通常,更好的方法是尽量让参数或返回值自身就足够清楚;但如果参数或返回值是某个标准库的一部分,或是你不能修改的代码,帮助阐释其含义的代码就会有用。
当然,这也会冒阐释性注释本身就不正确的风险。回头看看上例,你会发现想要确认注释的正确性有多难。这一方面说明了阐释有多必要,另外也说明了它有风险。所以,在写这类注释之前,考虑一下是否还有更好的办法,然后再加倍小心地确认注释正确性。
1.5 警示
有时,用于警告其他程序员会出现某种后果的注释也是有用的。例如,下面的注释解释了为什么要关闭某个特定的测试用例:
1.6 TODO 注释
有时,有理由用//TODO形式在源代码中放置要做的工作列表。
TODO是一种程序员认为应该做,但由于某些原因目前还没做的工作。它可能是要提醒删除某个不必要的特性,或者要求他人注意某个问题。它可能是恳请别人取个好名字,或者提示对依赖于某个计划事件的修改。无论TODO的目的如何,它都不是在系统中留下糟糕的代码的借口。
如今,大多数好IDE都提供了特别的手段来定位所有TODO注释,这些注释看来丢不了你不会愿意代码因为TODO的存在而变成一堆垃圾,所以要定期查看,删除不再需要的。
1.7 放大
注释可以用来放大某种看来不合理之物的重要性。
1.8 公共API中的Javadoc
没有什么比被良好描述的公共AP更有用和令人满意的了。标准Jav库中的 Javadoc就是一例。没有它们,写Java程序就会变得很难.
如果你在编写公共API,就该为它编写良好的 Javadoc。不过要记住本章中的其他建议。就像其他注释一样, Javadoc也可能误导、不适用或者提供错误信息
2. 坏的注释
大多数注释都属此类。通常,坏注释都是糟糕的代码的支撑或借口,或者对错误决策的修正,基本上等于程序员自说自话
2.1 喃喃自语
如果只是因为你觉得应该或者因为过程需要就添加注释,那就是无谓之举。如果你决定
写注释,就要花必要的时间确保写出最好的注释。
例如,我在 FitNesse中找到的这个例子,例中的注释大概确实有用。不过,作者太着急
或者没太花心思。他的喃喃自语变成了一个谜团。
catch代码块中的注释是什么意思呢?显然对于作者有其意义,不过并没有对其他看代码的人起好到足够的作用。
2.2 多余的注释
写在方法或者属性前,并且去掉也不影响理解的注释
eg:
//用户名称
public void userName;
//获取用户名称
public void getUserName(){
...
}
记住,注释的作用是无法通过代码表达时阐述代码意思,能用代码表达的时候,千万不要写多余的注释。
2.3 误导性注释
有时,尽管初衷可嘉,程序员还是会写出不够精确的注释。
存在误导的注释要比没有注释危害性大得多。
2.4 循规式注释
所谓每个函数都要有 Javadoc或每个变量都要有注释的规矩全然是愚盡可笑的。这类注释徒然让代码变得散乱,满口胡言,令人迷惑不解。
只有公用的API是必要加上Javadoc注释的。
没用的注释只会搞乱代码,有可能误导读者。
2.5 日志式注释
有人会在每次编辑代码时,在模块开始处添加一条注释。这类注释就像是一种记录每次修改的日志。我见过满篇尽是这类日志的代码模块。
很久以前,在模块开始处创建并维护这些记录还算有道理。那时,我们还没有源代码控制系统可用。如今,这种冗长的记录只会让模块变得凌乱不堪,应当全部删除。
2.6 废话注释
有时,你会看到纯然是废话的注释。它们对于显然之事喋喋不休,毫无新意。
eg:
public class Student{
//默认构造器
public Student(){
...
}
}
这类注释废话连篇,我们都学会了视而不见。读代码时,眼光不会停留在它们上面。最终,当代码修改之后,这类注释就变作了谎言一堆。用整理代码的决心替代创造废话的冲动吧。你会发现自己成为更优秀、更快乐的程序员。
2.7 位置标记
有时,程序员喜欢在源代码中标记某个特别位置。例如,最近我中看到这样一行:
把特定函数趸放在这种标记栏下面,多数时候实属无理。鸡零狗碎,理当删除——特别是尾部那一长串无用的斜杠。
这么说吧。如果标记栏不多,就会显而易见。所以,尽量少用标记栏,只在特别有价值的时候用。如果滥用标记栏,就会沉没在背景噪音中,被忽略掉。
2.8 归属与书名
源代码控制系统非常善于记住是谁在何时添加了什么。没必要用那些小小的签名搞脏代码。你也许会认为,这种注释大概有助于他人了解应该和谁讨论这段代码。不过,事实却是注释在那儿放了一年又一年,越来越不准确,越来越和原作者没关系重申一下,源代码控制系统是这类信息最好的归属地。
2.9 注释掉的代码
其他人不敢删除注释掉的代码。他们会想,代码依然放在那儿,一定有其原因,而且这段代码很重要,不能删除。注释掉的代码堆积在一起,就像破酒瓶底的渣滓一般。
我们无需用注释来标记,删掉即可,它们肯定丢不了的。
2.10 非本地信息
假如你一定要写注释,请确保它描述了离它最近的代码。别在本地注释的上下文环境中给出系统级的信息。
2.11 信息过多
别在注释中添加有趣的历史性话题或者无关的细节描述。下列注释来自某个用来测试base64编解码函数的模块。除了RFC文档编号之外,注释中的其他细节信息对于读者完全没有必要。
2.12 不明显的联系
注释及其描述的代码之间的联系应该显而易见。如果你不嫌麻烦要写注释,至少让读者能看着注释和代码,并且理解注释所谈何物。
注释中不要出现一个方法名、计算过程、特殊变量等不明显且不易理解的东西。
注释的作用是解释未能自行解释的代码。如果注释本身还需要解释,就太遗憾了。
2.13 函数头
短函数不需要太多描述。为只做一件事的短函数选个好名字,通常要比写函数头注释要好。
2.14 非公共代码的Javadoc
虽然 Javadoc对于公共API非常有用,但对于不打算作公共用途的代码就令人厌恶了为系统中的类和函数生成 Javadoc页并非总有用,而 Javadoc注释额外的形式要求几乎等同于八股文章。这与上述提高的废话注释相呼应。