注释就像是一把“双刃剑”,什么也比不上放置良好的注释来得有用;同时,什么也不会比乱七八糟的注释更有本事搞乱一个模块,什么也不会比陈旧、提供错误信息的注释更有破坏性。注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败。
1.注释不能美化糟糕的代码
写注释的常见动机之一是糟糕的代码的存在。
带有少量注释的整洁而有表达力的代码,比带有大量注释的零碎而复杂的代码像样的多。与其花时间编写解释你搞出的糟糕的代码的注释,不如花时间清洁那堆糟糕的代码。
2. 用代码来阐述
有时候,代码本身不足以解释其行为,比如下面两段代码:
代码段1:
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_DAY) &&
(employee.age > 65))
代码段2:
if (employee.isEligibleForFullBenefits())
很显然,代码段2更容易理解,很多时候,只需要创建一个描述与注释所言同一事物的函数即可。
3. 好注释
有些注释是必须的,也是有利的。不过要记住,唯一真正的好注释是你想办法不去写的注释。
(1)法律信息
有时,公司代码规范要求编写与法律有关的注释。这类注释不应是合同或法典,只要有可能,就指向一份标准许可或其他外部文档,而不要把所有条款放到注释中。
(2)提供信息的注释
// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
注释说明,该正则表达式意在匹配一个经由SimpleDateFormat.format函数利用特定格式字符串格式化的时间和日期。
这类注释有时管用,但更好的方式是尽量利用函数名称表达信息。
(3) 对意图的解释
有时,注释不仅提供了有关实现的有用信息,而且还提供了某个决定后面的意图。
(4) 阐释
当参数或者返回值晦涩难懂时,通常,更好的方法是尽量让参数或返回值自身就足够清楚;但如果参数或返回值是某个标准库的一部分,或是你不能修改的代码,帮助阐释其含义的代码就会有用
(5) 警示
有时,用于警示其他程序员会出现某种后果的注释也是有用的。
(6)TODO注释
有时,有理由用//TODO 形式在源代码中放置要做的工作列表。
TODO是一种程序员认为应该做,但由于某些原因目前还没做的工作。它可能是要提醒删除某个不必要的特性,或者要求他人注意某个问题。
(7) 放大
注释可以用来放大某种看起来不合理之物的重要性。
String listItemContent = match.group(3).trim();
// the trim is real important. It removes the starting
// spaces that could cause the item to be recognized
// as another list
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));
4. 坏注释
大多数注释都属此类。通常,坏注释都是糟糕代码的支撑或借口,或者对错误决策的修正,基本上等于程序员自说自话。
(1)喃喃自语
如果只是因为你觉得应该或者因为过程需要就添加注释,那就是无谓之举。
(2)多余的注释
若注释不能比代码本身提供更多的信息,它没有证明代码的意义,也没有给出代码的意图或逻辑,读它并不比读代码更容易。
(3)误导性注释
有时,不精确的注释会有一定的误导“功效”
(4)循规式注释
所谓每个函数都要有Javadoc或每个变量都要有注释的规矩全然是愚蠢可笑的。这类注释突然让代码变得散乱,满口胡言,令人迷惑不解。
/**
* @param title The title of the CD
* @param author The author of the CD
* @param tracks The number of tracks on the CD
* @param durationInMinutes The duration of the CD in minutes
*/
public void addCD(String title, String author, int tracks, int durationInMinutes) {
CD cd = new CD();
cd.title = title;
cd.author = author;
cd.tracks = tracks;
cd.duration = durationInMinutes;
cdList.add(cd);
}
(5) 日志式注释
有人会在每次编辑代码时,在模块开始处添加一条注释。这类注释就像是一种记录每次修改的日志。
如今,这种冗长的记录只会让模块变得凌乱不堪,应当全部删除。
(6)废话注释
有时,会有完全是废话的注释。它们对于显然之事喋喋不休,毫无新意。
(7)可怕的废话
Javadoc也可能是废话。
/** The name. */
private String name;
/** The version */
private String version;
/** The licenseName */
private String licenseName;
/** The version. */
private String info;
(8)能用函数或变量时就别用注释
(9)标记位置
有时,程序员喜欢在源代码中标记某个特别位置。例如:
// Actions //
把特定函数放在这种标记下面,多数时候实属无理。鸡零狗碎,理当删除——特别是尾部那一长串无用的斜杠。
如果滥用标记,代码就会沉没在背景噪音中,被忽略掉。
(10)括号后面的注释
有时,程序员会在括号后面放置特殊的注释。尽管这对于含有深度嵌套结构的长函数可能有意义,但只会给我们更愿意编写的短小、封装的函数带来混乱。如果你发现自己想标记右括号,其实应该做的是缩短函数。
(11)归属与署名
版本控制系统非常善于记住是谁在何时添加了什么。没必要用小小的签名搞脏代码。
(12)注释掉的代码
注释掉的代码如果无用,请立即删掉,不然其他人对于注释掉的代码无从下手,不知道其重要性,不敢删除。
(13)非本地信息
假如你一定要写注释,请确保它描述了离它最近的代码。别在本地注释的上下文环境中包含出系统级的信息。
下面的例子,除了可怕的冗余之外,还包含了系统级的默认端口信息。但是这个函数完全没控制那个所谓的默认值。假如那个值更改了,无法担保这个注释也会跟着修改。
/**
* port on which fitnesse would run. Default to <b>8082</b>
* @param fitnessePort
*/
public void setFitnessePort(int fitnessPort) {
this.fitnessePort = fitnessePort;
}
(14)信息过多
别在注释中添加有趣的历史性话题或者无关的细节描述。
(15)不明显的联系
注释及其描述的代码之间的联系应该是显而易见。如果你不嫌麻烦要写注释,至少让读者能看着注释和代码,并且理解注释所谈何物。
(16)函数头
短函数不需要太多描述。为只做一件事的短函数取个好名字,通常要比写函数头注释要好。
(17)非公共代码中的Javadoc
Javadoc对于公共API非常有用,但对于不打算做公共用途的代码就令人厌恶了。为系统中的类和函数生成Javadoc页并非总有用。