背景
- 程序员在日常开发中,经常性需要对代码功能进行相关注释。但是否思考过:
- 什么情况下需要注释?
- 什么样的注释是好注释?
- 什么样的注释是糟糕的?
- 最近在阅读《代码整洁之道》第4章 注释,结合日常的开发经验,对上述问题进行思考并进行总结
注释的时机
- 凡是能够用代码表达意图的,就不要用注释了
- 因为代码是唯一准确的信息来源,代码在变动,演进,但注释不一定随之变更,时间越久,代码经手的人越多,越可能出现注释与代码功能不一致的情况。此时,注释可能带来严重的误导
- 写注释前,先思考下,能否通过模块、类名、函数、变量名来描述代码的意图。时刻把握一个原则:代码自解释,如非必要,尽量不要写注释
举例
旧代码
if (StringUtils.equals(languageCode, "zh-CN")) {}
- 上述代码中,注释说明了默认语种下,执行xxx逻辑。但存在几个问题:
- zh-CN是一个魔法值,并不能明确的知道代表默认语种。可以通过变量名进行优化
- StringUtils.equals(languageCode, "zh-CN")的条件含义不够直观,要通过看注释才知道是什么意思。可以通过工具类及方法名来进行优化
- 后续代码迭代后,如果en变成默认语种,这时候改了代码中的zh-CN,而注释中的没改,就会给人带来误导
优化后的代码
public static final String DEFAULT_LANGUAGE = "zh-CN";
boolean isDefaultLanguage = LanguageUtils.isDefaultLanguage(languageCode);
if (isDefaultLanguage) {}
- 可以明显的看出,经过优化的代码,可读性,可维护性都是比旧代码要好很多的
什么是好注释?
标准
- 能够给团队开发者 提供代码之外的准确信息 或 降低对复杂代码功能的理解难度 的注释
举例
- 开发过程中,可能会使用正则去匹配某个规则的文本,如果这个正则可读性较差,在正则上进行注释可有效的降低团队其他开发对该代码的理解难度
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
- 项目经过多次迭代中,可能会有一些特殊的校验(历史遗留),这个时候,注释可以让团队开发人员清晰的明白校验存在的意义
- 强调类型的注释。例如:强调某些类不适用多线程、某个list不能传不可变的list等
- TODO类型的注释。TODO是一种程序员认为应该做,但由于某些原因目前还没做的工作。可通过IDEA的TODO视图快速查找到TODO注释。但不能大量滥用TODO,TODO注释要定期处理
- 公共API中的javadoc注释。有利于调用方快速的了解代码的功能
什么是糟糕的注释?
标准
举例
- 在开发过程中,经常性遇到的糟糕代码主要是以下几类:
- 注释掉的代码。把代码注释掉,后续其他开发维护的时候,可能觉得这个代码依然放在这,肯定有什么原因,从而不敢删除注释掉的代码。但注释的代码很可能只是谁忘记清理了。所以建议对于不用的代码,直接删除。后续如果有需要,通过git进行找回
- 废话注释。废话类的注释一般是代码已经能表达意图了,却多此一举进行注释。这类注释在代码迭代的过程中,很容易出现注释和代码不一致的情况。例如:
public AnnualDateRule() {}
- 文不对题的注释。在软件迭代的过程中,很容易出现这种注释。所以能用代码命名等方式表达意图的,就尽量不要再写注释了。同时,这类注释也可能出现在编写同类功能时,复制粘贴的过程中。例如:
private String version;
private String info;
这里info属性上的注释,应该就是拷贝的时候,忘记改注释导致的
- 喃喃自语的注释。该类注释往往只有写注释的人才能理解,并且这类注释往往过段时间,可能写注释的人也不知道什么含义。例如:
这个注释中的“安全隐患”没具体指明,只有当事人才知道怎么回事,对其他开发者只会留下疑惑:安全隐患是啥???我改这部分代码会不会导致啥问题??
- 循规式注释。所谓每个函数都要有 Javadoc 或每个变量都要有注释的规矩全然是愚蠢可笑的。这类注释徒然让代码变得散乱,满口胡言,令人迷惑不解。例如,要求每个函数都要有 Javadoc,就会得到类似如下的代码:
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 = duration;
cdList.add(cd);
}
这类废话只会搞乱代码,有可能误导读者。非公共api的方法,建议用命名和变量名来表达方法能力
总结
- 写注释前,先思考下,能否通过模块、类名、函数、变量名来描述代码的意图
- 时刻把握一个原则:代码自解释,如非必要,尽量不要写注释