在以前一些关于代码注释的文章中,我发现,你不需要的注释才是***的注释。不要急着批判,请允许我阐述一下。首先代码应该尽量地简洁,尽可能地做到不需要依赖注释就可以理解。只有那些真的没法更易于理解的代码,才需要我们添加注释。
有一本非常经典的书叫《Structure and Interpretation of Computer Programs》(《电脑程序的结构和编译》),最初发表于1985年,在序言中就表明其观点:
程序必须能便于我们阅读,让机器执行只是附带的。
Knuth也在他1984年发表的经典论文《Literate Programming》(《文学编程》)中秉持了类似的观点:
我们应该转变传统的思维,程序不再是告诉计算机做什么的指令,而是向人类描述如何让计算机做事情的文字,编程像写文章一样。
文学编程关注的主要是展现精致的风格。程序员应该像作家一样,认真地选择变量名,并解释每个变量的意思,努力写出易于人脑理解的程序。
如果你写出来的代码,既能被其他程序员理解又能成功编译,那么需要添加注释的地方肯定就不会很多。关于使用注释作为辅助工具,下面就是一个颇具代表意义的例子:
这段代码取自一个已经使用多年、闭源了的系统。
- float _x = abs(x - deviceInfo->position.x) / scale;
- int directionCode;
- if (0 < _x & x != deviceInfo->position.x) {
- if (0 > x - deviceInfo->position.x) {
- directionCode = 0x04 /*left*/;
- } else if (0 < x - deviceInfo->position.x) {
- directionCode = 0x02 /*right*/;
- }
- }
上面的代码等同于下面的代码,但是下面的代码可读性更高。
- static const int DIRECTIONCODE_RIGHT = 0x02;
- static const int DIRECTIONCODE_LEFT = 0x04;
- static const int DIRECTIONCODE_NONE = 0x00;
- int oldX = deviceInfo->position.x;
- int directionCode = (x > oldX) ? DIRECTIONCODE_RIGHT : (x > oldX) ? DIRECTIONCODE_LEFT : DIRECTIONCODE_NONE;
需要注意的是,注释的越多并不意味着代码的理解性更强。当然在本案例中并没有涉及到这一点。上面的注释——如果你注意到的话——使得代码更加显得凌乱不堪。有时候,注释得越精简代码的可读性才越高。特别是在你必须更换使用其他符号名的情况下。
虽然我们能无限次地重构和精简代码以避免写繁冗的注释,但是表述自己的思考过程的方式却是有局限性的。
无论***你呈现的代码是有多么的简洁和清楚,代码也不可能完全自文档化。但是代码永远也不可能取缔注释的存在。正如Jef Raskin所说:
[代码]无法解释如此写程序以及选择该方法的原因。从[代码]上我们也看不出选择某些替代方法的理由。例如:
- /* A binary search turned out to be slower than the Boyer-Moore algorithm for the data sets of interest, thus we have used the more complex, but faster method even though this problem does not at first seem amenable to a string search technique. */
在A开发人员看来完全显而易见的东西,可能在B眼里完全就像雾里看花一样。所以我们在写注释的时候,也要考虑到这一点:
下面这个可能你一清二楚
- $string = join('',reverse(split('',$string)));
反转字符串,但是要如何才能插入到
- # Reverse the string
Perl文件中呢?
的确,这一点都不难。归根究底,代码只会告诉你程序是如何工作的,但是注释则能说明工作的原因。所以,下次你写代码的时候,不妨在这两个方面给你的同事做个榜样。
英文原文:Code Tells You How, Comments Tell You Why