这5种代码注释,让你看起来像个入门级程序员

764 阅读13分钟

One of the first things you learn to do incorrectly as a programmer is commenting your code. My experience with student and recently graduated programmers tells me that college is a really good place to learn really bad code commenting techniques. This is just one of those areas where in-theory and in-practice don't align well.

作为程序员,你第一件学会做错的事情之一就是代码注释。我和学生以及刚毕业的程序员工作的经历告诉我,大学实在是一个学习错误的代码注释技巧的好地方。代码注释是许多理论和实践并不能很好统一的领域之中的一个。

There are two factors working against you learning good commenting technique in college.

  • Unlike the real world, you do a lot of small one-off projects as a solo developer. There's no one out there fantasizing about dropping a boulder on you for making them decipher your coding atrocity.
  • That commenting style you are emulating from your textbook is only a good practice when the comments are intended for a student learning to program. It is downright annoying to professional programmers.

有两个原因使得你没法在大学里学会好的代码注释的技巧。 第一,大学里你通常做很多一个人完成的一次性项目,而这和现实世界完全不一样。因此并不会有人因为要破解你的垃圾代码而产生拿块砖头砸你的想法。第二,你模仿的教科书里的那种注释风格,是专门给正在学编程的学生看的,而这种风格对于靠编程吃饭的程序员来说,就太惹人讨厌了。

These tips are primarily intended for upstart programmers who are transitioning into the real world of programming, and hopefully will prevent a few from looking quite so n00bish during their first code review. Code Review? Oh yeah, that's something else they didn't teach you in school, but that's a whole other article, I'll defer to Jason Cohen on that one.

So let's get started…

这些技巧主要是给刚刚进入现实的编程世界的入门程序员的,希望可以帮他们在第一次代码审核的时候看起来不要那么像个新兵蛋子。代码审核?对,这是另一件大学里没有教你的东西,不过这需要另一片文章了。我会推荐Jason Cohen的文章。 那就让我们开始吧

Comments are not subtitles

It's easy to project your own worldview that code is a foreign language understood only by computers, and that you are doing the reader a service by explaining what each line does in some form of human language. Or perhaps you are doing it for the benefit of that non-programmer manager who will certainly want to read your code (Spoiler: He won't).

注释不是副标题

我们会倾向于认为计算机代码就像是只有计算机能理解的一种外语,因此当你用一种人类语言向读者解释每行代码在做什么时,是帮了他们一个大忙。或者你写注释是为了你的非程序员经理将来读你的代码时更容易(剧透一下,他不会读的)。

Look, in the not too distant future, you will be able to read code almost as easily as your native language, and everyone else who will even glance at it almost certainly already can. By then you will realize how silly it is to write comments like these:

但是记住,在不久的将来,你阅读代码就会变得像阅读母语一样容易,每一个哪怕只会瞥一眼你的代码的人也一样。那时候你就会明白类似这样的代码看起来是多么的愚蠢。

// Loop through all bananas in the bunch
foreach(banana b in bunch) {
    monkey.eat(b);  //make the monkey eat one banana
}

You may have been taught to program by first writing pseudo-code comments then writing the real code into that wire-frame. This is a perfectly reasonable approach for a novice programmer. Just be sure to replace the comments with the code, and don't leave them in there.

Exceptions:

Code examples used to teach a concept or new programming language.

Programming languages that aren't remotely human readable (Assembly, Perl)

也许学校曾教你先写一段伪代码的注释,然后再写上真的代码。对于新手来说这是一个合理的建议,但是记住用代码代替这些伪代码注释,而不是让他们留在那里。

一些例外: 某些代码实例用来解释一种新概念或者新的编程语言 某些可读性非常差的语言(例如汇编或者Perl)

Comments are not an art project

This is a bad habit propagated by code samples in programing books and open source copyright notices that are desperate to make you pay attention to them.

/*
   _     _      _     _      _     _      _     _      _     _      _     _
  (c).-.(c)    (c).-.(c)    (c).-.(c)    (c).-.(c)    (c).-.(c)    (c).-.(c)
   / ._. \      / ._. \      / ._. \      / ._. \      / ._. \      / ._. \
 __\( Y )/__  __\( Y )/__  __\( Y )/__  __\( Y )/__  __\( Y )/__  __\( Y )/__
(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
   || M ||      || O ||      || N ||      || K ||      || E ||      || Y ||
 _.' `-' '._  _.' `-' '._  _.' `-' '._  _.' `-' '._  _.' `-' '._  _.' `-' '._
(.-./`-'\.-.)(.-./`-'\.-.)(.-./`-'\.-.)(.-./`-'\.-.)(.-./`-'\.-.)(.-./`-'\.-.)
 `-'     `-'  `-'     `-'  `-'     `-'  `-'     `-'  `-'     `-'  `-'     `-'
 
                 -It's Monkey Business Time! (Version 1.5)
*/

Why, that's silly. You'd never do something so silly in your comments.

ORLY? Does this look familiar?

注释不是一个艺术品

这是一个被迫切希望引起你关注的编程书上的代码或者开源项目的版权声明所传播开来的坏习惯。 为什么?因为这很蠢。你应该永远不会在你的代码里加这种注释。

哦,真的吗?这个看起来是不是有点眼熟?

+------------------------------------------------------------+
 | Module Name: classMonkey                                   |
 | Module Purpose: emulate a monkey                           |
 | Inputs: Bananas                                              |
 | Outputs: Grunts                                            |
 | Throws: Poop                                               |
 +------------------------------------------------------------+

Programmers love to go "touch up" their code to make it look good when their brain hurts and they want something easy to do for a while. It may be a waste of time, but at least they are wasting it during periods where they wouldn't be productive anyway.

程序员在脑细胞受损的时候喜欢做点容易的事情,比如把他们的代码“美化”一下。这样也许是浪费时间,不过至少他们是在本来也就没什么生产力的间隙在浪费时间。

The trouble is that it creates a time-wasting maintenance tax imposed on anyone working with the code in the future just to keep the pretty little box intact when the text ruins the symmetry of it. Even programmers who hate these header blocks tend to take the time to maintain them because they like consistency and every other method in the project has one.

How much is it bugging you that the right border on that block is misaligned? Yeah. That's the point.

不过问题是他们给将来要维护这个代码的人加上了一笔需要浪费不少时间的维护税,也许只是为了在文字破坏了对称性的情况下保住这个漂亮的小方块。即使是那些讨厌这类头部注释块的程序员也会花时间维护他们,因为项目里的每一个方法都有这么一段,而他们只是喜欢保持一致。

如果这个注释块的右边框没有对齐你会不会觉得很不爽?对,就是这个意思。

Header Blocks: Nuisance or Menace?

This one is going to be controversial, but I'm holding my ground. I don't like blocks of header comments at the top of every file, method or class.

Not in a boat, not with a goat.

Why? Well let me tell you, George McFly…

头部注释块:只是不便还是有害?

这条肯定会引起争论,但我还是坚持自己的立场。我不喜欢在每个文件,方法,或者类的最前面的头部加上注释块。 一点也不喜欢。 为什么?我来告诉你。

They are enablers for badly named objects/methods – Of course, header blocks aren't the cause for badly named identifiers, but they are an easy excuse to not put in the work to come up with meaningful names, an often deceptively difficult task. It provides too much slack to just assume the consumer can just read the “inline documentation” to solve the mystery of what the DoTheMonkeyThing method is all about.

JohnFx's Commandment: The consumer of thy code should never have to see its source code to use it, not even the comments.

因为他们会带来有着坏名字的对象或者方法。当然,他们本身不是造成坏名字的原因,但是他们给不花时间好好想个好名字有了一个轻松的借口,而想个好名字其实是一个看起来很容易其实却很难的事情。如果我们相信读代码的人会去读我们的“代码内注释”来试图理解这个DoTheMonkeyThing方法在做什么,那我们想得就太理想化了。

They never get updated: We all know that methods are supposed to remain short and sweet, but real life gets in the way and before you know it you have a 4K line class and the header block is scrolled off of the screen in the IDE 83% of the time. Out of sight, out of mind, never updated.

The bad news is that they are usually out of date. The good news is that people rarely read them so the opportunity for confusion is mitigated somewhat. Either way, why waste your time on something that is more likely to hurt than help?

Exception: Some languages (Java/C#) have tools that can digest specially formatted header block comments into documentation or Intellisense/Autocomplete hints. Still, remember rule (2) and stick to the minimum required by the tool and draw the line at creating any form of ASCII art.

另外,这些注释块永远不会被更新。我们都知道每个方法都要保持简短,但现实是,在你自己发现之前,你有一个超过4k行的类,然后你的头部注释块在83%的时间里都在IDE屏幕的外面。看不见,也想不到,因此永远不会被更新。

坏消息是他们通常是过时的。好消息是人们反正很少去读他们,因此被误解的概率某种程度上降低了。不管怎样,为什么要浪费时间在一些更可能带来伤害而不是帮助的事情上呢?

例外:某些语言(例如Java或C#)有工具可以把一些特殊格式的注释块转换成文档或者自动补全的提示。但还是记住第二条建议,尽量不要过度,避免开始创作某种ASCII艺术画。

Comments are not source control

This issue is so common that I have to assume that programmers (a) don't know how to use source control; or (b) don't trust it.

注释不是版本控制

这个问题是如此常见,我不得不假设这些程序员(a)不知道怎么用版本控制,或者(b)根本不信任版本控制。

Archetype 1: “The Historian”

// method name: pityTheFoo (included for the sake of redundancy)
// created: Feb 18, 2009 11:33PM
// Author: Bob
// Revisions: Sue (2/19/2009) - Lengthened monkey's arms
//            Bob (2/20/2009) - Solved drooling issue
 
void pityTheFoo() {
     ...
}

The programmers involved in the evolution of this method probably checked this code into a source control system designed to track the change history of every file, but decided to clutter up the code anyway. These same programmers more than likely always leave the Check-In Comments box empty on their commits.

I think I hate this type of comment worst of all, because it imposes a duty on other programmers to keep up the tradition of duplicating effort and wasting time maintaining this chaff. I almost always delete this mess from any code I touch without an ounce of guilt. I encourage you to do the same.

“历史学家型” 开发和维护这段代码的程序员们很可能已经把代码录入了用来追踪文件历史的版本控制系统,但他们还是决定在代码里塞进这些注释。这批程序员可能正是在代码提交的信息栏里面留下空白的人。

我觉得这种注释是我最讨厌的,因为它给其他的程序员强加了一份保持这个传统的责任,让他们浪费时间做出重复劳动。无论什么时候看到这样的注释,我总是毫无负罪感的删掉它们。我鼓励你也这样做。

Archetype 2: “The Code Hoarder”

void monkeyShines() { if (monkeysOnBed(Jumping).count > max) { monkeysOnBed.breakHead();

    // code removed, smoothie shop closed.
    // leaving it in case a new one opens.
    // monkeysOnBed.Drink(BananaSmoothie);
 }

}

Another feature of any tool that has any right to call itself a SCM is the ability to recover old versions of code, including the parts you removed. If you want to be triple super extra sure, create a branch to help you with your trust issues.

“舍不得扔代码型” 任何可以被称为版本控制系统的工具都有一个功能就是恢复文件的历史版本,包括你删掉的部分。如果你真的想超级超级超级确定不会弄丢东西,你也可以建一个分支解决你的信任问题。

Comments are a code smell

Comments are little signposts in your code explaining it to future arhaeologists that desperately need to understand how 21st century man sorted lists of purchase orders.

Unfortunately, as Donald Norman explained so brilliantly in The Design of Everyday Things, things generally need signs because their afforances have failed. In plain English, when you add a comment you are admitting that you have written code that doesn't communicate its purpose well.

Despite what your prof told you in college, a high comment to code ratio is not a good thing. I'm not saying to avoid them completely, but if you have a 1-1 or even a 5-1 ratio of LOC to comments, you are probably overdoing it. The need for excessive comments is a good indicator that your code needs refactoring.

Whenever you think, "This code needs a comment" follow that thought with, "How could I modify the code so its purpose is obvious?" Talk with your code, not your comments.

注释是代码深层问题的一种表现

注释就像是代码中的标记,用来向那些迫切想了解21世纪的人类怎么对订单进行排序的未来的考古学家解释你的代码。不幸的是,正像Donal Newman在 The Design of Everyday Things中解释的那样,物品需要标记因为他们自身的功能性没有得到体现。用通俗的语言来说,当你添加一段注释时,你就在承认你写了一段没有很好地表达清楚作用的代码。

不像你的大学教授告诉你的那样,较高的注释对代码比例并不是一件好事。我不是说要完全避免,但是如果代码对注释的比例达到1:1甚至5:1,也许你有点做过头了。需要过量的注释代表这你的代码可能需要重构了。

每当你想“这段代码需要一个注释”时,马上问自己“我怎么才能让这段代码的作用更加明显?” 和你的代码交流,而不是和你的注释。

Technique 1: Use meaningful identifiers and constants (even if they are single use)

技巧1:使用有意义的变量和常数,即使可能只用到一次


// Before
 // Calculate monkey's arm length
 // using its height and the magic monkey arm ratio
 double length = h * 1.845; //magic numbers are EVIL!
 
// After - No comment required
double armLength = height * MONKEY_ARM_HEIGHT_RATIO;

Technique 2: Use strongly typed input and output parameters

技巧2:在输入输出中使用强类型参数

// Before
 // input parameter monkeysToFeed:
 // DataSet with one table containing two columns
 //     MonkeyID (int) the monkey to feed
 //     MonkeyDiet (string) that monkey's diet
 void feedMonkeys(DataSet monkeysToFeed) {
 }
 
//  After: No comment required
void feedMonkeys(Monkeys monkeysToFeed) {
}

Technique 3: Extract commented blocks into another method

技巧3:把需要注释的代码块独立出来``

// Before
 
 // Begin: flip the "hairy" bit on monkeys
 foreach(monkey m in theseMonkeys) {
     // 5-6 steps to flip bit.
 }
 // End: flip the "hairy" bit on monkeys
 
// After No comment required
flipHairyBit(theseMonkeys);

As an added bonus, technique 3 will tend to reduce the size of your methods and minimizing the nesting depth (see also "Flattening Arrow Code") all of which contribute to eliminating the need for commenting the closing tags of blocks like this:

作为一个附加的好处,技巧3通常会减少你的函数的规模,并减少嵌套的深度,而这又可以避免你需要在每层嵌套的结束的地方加上更多的注释

          } // ... if see evil
       } // ... while monkey do.
    } // ... if monkey see.
  } // ... class monkey
} // ... namespace primate

本期的文摘就到这里,我们会在将来持续提供这样的文摘精读,另外我们还会有更多的让大家练习写作和口语的活动,欢迎大家关注我们的专栏或者微型公众号“开发者文摘”,也可以在公众号留言给出你关于程序员应该如何学好英语的建议和反馈 欢迎关注我们的公众号"开发者文摘",通过技术文章学习英语,打开更广阔的世界!