目录
4.0 前言
-
什么也比不上放置良好的注释来得有用。什么也不会比乱七八糟的注释更有本事搞乱一个模块。什么也不会比陈旧、提供错误信息的注释更有破坏性。
-
注释会撒谎。也不是说总是如此或有意如此,但出现得实在太频繁。注释存在的时间越久,就离其所描述的代码越远,越来越变得全然错误。原因很简单。程序员不能坚持维护注释。
-
真实只在一处地方有:代码。只有代码能忠实地告诉我们它做的事情。那是唯一真正准确的信息来源。所以,尽管有时也需要注释,我们也该多花心思尽量减少注释量。
4.1 注释不能美化糟糕的代码
带有少量注释的整洁而有表达力的代码,要比带有大量注释的零碎而复杂的代码像样的多。与其花时间编写解释你搞出的糟糕的代码的注释,不如花时间清洁那堆糟糕的代码。
4.2 用代码来阐述
有时,代码本身不足以解释其行为。不幸的是,许多程序员据此以为,代码很少——如果有的话——能做好解释工作。这种观点纯属错误。只要想上那么几秒钟,就能用代码解释你大部分的意图。
很多时候,简单到只需要创建一个描述与注释所言同一事物的函数即可。
//愿意看到这个
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) &&(employee.age > 65))
//还是这个?
if (employee.isEligibleForFullBenefits())
4.3 好注释
有些注释是必须的,也是有利的。来看看一些我认为值得写的注释。不过要记住,唯一真正好的注释是你想办法不去写的注释。
4.3.1 法律信息
有时,公司代码规范要求编写与法律有关的注释。“// Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.”
4.3.2 提供信息的注释
有时,用注释来提供基本信息也有其用处。
// format matched kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
4.3.3 对意图的解释
有时,注释不仅提供了有关实现的有用信息,而且还提供了某个决定后面的意图。
4.3.4 阐释
有时,注释把某些晦涩难明的参数或返回值的意义翻译为某种可读形式,也会是有用的。通常,更好的办法是尽量让参数或返回值自身就足够清楚;但如果参数或返回值是某个标准库的一部分,或是你不能修改的代码,帮助阐释其含义的代码就会有用。
4.3.5 警示
有时,用于警告其他程序员会出现某种后果的注释也是有用的。
/**
* Autogenerated by Thrift Compiler (0.8.0)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
*/
//code
......
4.3.6 TODO注释
-
有时,有理由用//TODO形式在源代码中放置要做的工作列表。
-
TODO是一种程序员认为应该做,但由于某些原因目前还没做的工作。它可能是要提醒删除某个不必要的特性,或者要求他人注意某个问题。
-
它可能是恳请别人取个好名字,或者提示对依赖于某个计划事件的修改。无论TODO的目的如何,它都不是在系统中留下糟糕代码的借口。
-
大多数好的IDE都提供了手段来定位所有的TODO注释,这些注释看来丢不了。你不会愿意代码因为TODO的存在而变成一堆垃圾,所以要定期查看,删除不再需要的。
4.3.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.3.8 公共API中的Javadoc
没有什么比被良好描述的公共API更有用和令人满意的了。标准Java库中的Javadoc就是一例。没有它们,写Java程序就会变得很难。
4.4 坏注释
大多数注释都属此类。通常,坏注释都是糟糕的代码的支撑或借口,或者对错误决策的修正,基本上等于程序员自说自话
4.4.1 喃喃自语
如果只是因为你觉得应该或者因为过程需要就添加注释,那就是无谓之举。如果你决定写注释,就要花必要的时间写出好的注释。
4.4.2 多余的注释
如果读注释花的时间比读代码的长,那么这里的注释就太糟糕了。
4.4.3 误导性注释
4.4.4 循规式注释
所谓每个函数都要有Javadoc或每个变量都要有注释的规矩是愚蠢可笑的。这类注释徒然让代码变得散乱,满口胡言,令人迷惑不解。
4.4.5 日志式注释
-
在模块开始处添加一条注释。这类注释就像是一种记录每次修改的日志。
-
很久以前,在模块开始处创建并维护这些记录还算有道理。那时,我们还没有源代码控制系统可用。如今,这种冗长的记录只会让模块变得凌乱不堪,应当全部删除。
4.4.6 废话注释
这类注释废话连篇,我们都学会了视而不见。读代码时,眼光不会在它们上面停留。最终,当代码修改之后,这类注释就变作了谎言一堆。
4.4.7 可怕的废话
仔细阅读下面例子注释,你会发现剪切-粘贴错误。作者在写(或粘贴)注释时都没花心思,怎么指望读者从中获益呢?
** The name. */
private String name;
/** The version. */
private String version;
/** The licenceName. */
private String licenceName;
/** The version. */
private String info;
4.4.8 能用函数或变量时就别用注释
// does the module from the global list <mod> depend on the
// subsystem we are part of?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))
//可以改成以下没有注释的版本:
ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))
4.4.9 位置标记
如果标记栏不多,就会显而易见。所以,尽量少用标记栏,只在特别有价值的时候用。如果滥用标记栏,就会沉没在背景噪音中,被忽略掉。如:
// Actions //
4.4.10 括号后面的注释
-
有时,程序员会在括号后面放置特殊的注释,尽管这对于含有深度嵌套结构的长函数可能有意义,但只会给我们更愿意编写的短小、封装的函数带来混乱。
-
如果你发现自己想标记右括号,其实应该做的是缩短函数。
4.4.11 归属与署名
-
源代码控制系统非常善于记住是谁在何时添加了什么。没必要用那些小小的签名搞脏代码。
-
你也许会认为,这种注释大概有助于他人了解应该和谁讨论这段代码。不过,事实却是注释在那儿放了一年又一年,越来越不准确,越来越和原作者没关系。
-
重申一下,源代码控制系统是这类信息最好的归属地。
4.4.12 注释掉的代码
同上,源代码控制系统可以取代对代码的注释掉,前者可以为我们记住不要的代码。我们无需再用注释来标记,删掉即可。
4.4.13 HTML注释
IDE中的代码本来易于阅读,却因为HTML注释的存在变得难以卒读。
4.4.14 非本地信息
如果你一定要写注释,请确保它描述了离它最近的代码。别在本地注释的上下文环境中给出系统级的信息。
4.4.15 信息过多
别在注释中添加有趣的历史性话题或者无关的细节描述。
4.4.16 不明显的联系
注释及其描述的代码之间的联系应该显而易见。如果你不嫌麻烦要写注释,至少让读者能看着注释和代码,并且理解注释所谈何物。
4.4.17 函数头
短函数不需要太多描述。为只做一件事的短函数选个好名字,通常要比写函数头注释要好。
4.4.18 非公共代码中的Javadoc
虽然Javadoc对于公共API非常有用,但对于不打算作公共用途的代码就令人生恶了。