我有一个同事,他坚持认为他的代码不需要注释,这是“自文档”。
我已经审阅了他的代码,虽然它比我看到的其他人编写的代码更清晰,但我仍然不同意自文档化代码与经过注释和文档化的代码一样完整和有用。
帮我理解一下他的观点。
什么是自文档代码
它真的能取代注释良好和文档化的代码吗
在某些情况下,它是否比有良好文档和注释的代码更好
是否存在代码不可能在没有注释的情况下自文档化的例子
也许这只是我自身的局限性,但我看不出这怎么能成为一种好的练习。
这并不是一个争论——请不要提出为什么注释良好并有文档记录的代码是高优先级的原因——有很多资源都表明了这一点,但它们对我的同行来说并没有说服力。我认为我需要更全面地了解他的观点,才能说服他。如果你有必要,可以提出一个新的问题,但不要在这里争论。
另外,那些反对自我记录代码的人——这主要是为了帮助我理解自我记录代码传播者的观点(即积极的方面)。
自我记录代码是“DRY”(不要重复自己)的一个很好的例子。不要在注释中重复代码本身中的信息。
与其解释变量的用途,不如重命名变量。
与其解释一个简短的代码片段做什么,不如将其提取到一个方法中并给它一个描述性的名称(可能是注释文本的缩短版本)。
与其解释一个复杂的测试做什么,不如把它也提取到一个方法中,并给它起个好名字。
Etc.
在此之后,您将得到不需要太多解释的代码,它可以自行解释,因此您应该删除代码中只是重复信息的注释。
这并不意味着你完全没有注释,有一些信息你不能放入代码中,比如关于意图的信息(“为什么”)。在理想的情况下,代码和注释相互补充,每个注释都增加了独特的解释价值,而不会重复另一个注释中的信息。
自文档代码是非常清晰的代码,以至于不需要注释。我举个小例子:
//iterate from 0 to 100
for(int i=0; i < 100; i++) {
println i
}
注释没什么用,因为代码很清楚。文档是一个很好的实践,但是额外的文档会给代码增加不必要的干扰。你的同事需要知道的是,不是每个人都能阅读别人的代码并了解所有的细节。
int calc(int a, int b) {
return sqrt(a*a + b*b); //pythagoras theorem
}
如果没有注释,最后一个片段将很难破译。你可以想象其他更做作的例子。
我曾经和一个家伙一起工作,他打算把金融套件卖给一家大公司。他们坚持让他记录源代码,他写了一个30多页的汇编程序,并说“这是有记录的,看”——然后他翻到第13页,有一条评论“bump counter by one”。
伟大的产品,伟大的实现者,但是……
无论如何,在我看来,上面的重要评论是为了设置上下文。这段代码是自记录的:
> from BeautifulSoup import
> BeautifulSoup, Tag def
> replace_a_href_with_span(soup):
> links = soup.findAll("a")
> for link in links:
> tag = Tag(soup, "span", [("class", "looksLikeLink")])
> tag.contents = link.contents
> link.replaceWith(tag)
但是,就我个人而言,需要一个背景来充分理解它。
对于许多有效的答案,我想再提供一个观点:
什么是源代码?什么是编程语言?
机器不需要源代码。他们很高兴运行组装。编程语言是为了我们的利益。我们不想写汇编。我们需要理解我们在写什么。编程就是写代码。
你能读懂你写的东西吗?
源代码不是用人类语言编写的。它已经被尝试过(例如FORTRAN),但并不完全成功。
源代码不能有歧义。这就是为什么我们必须在其中加入比文本更多的结构。文本只适用于上下文,当我们使用文本时,我们认为这是理所当然的。源代码中的上下文总是存在的。想想c#中的“使用”。
大多数编程语言都有冗余,这样编译器就能在我们不连贯的时候发现我们。其他语言使用更多的推理,并试图消除冗余。
类型名、方法名和变量名在计算机中是不需要的。它们是供我们参考的。编译器不理解语义,这是我们要用的。
编程语言是人与机器之间的语言桥梁。它必须对我们来说是可写的,对他们来说是可读的。次要要求是它应该对我们来说是可读的。如果我们擅长语义,并且擅长构建代码,那么即使对我们来说,源代码也应该很容易阅读。最好的代码不需要注释。
但是复杂性潜伏在每个项目中,您总是需要决定将复杂性放在哪里,以及吞下哪只骆驼。这些是使用注释的地方。