自我记录代码与 注释代码


22

我进行了搜索,但是找不到我想要的东西,如果已经提出这个问题,请随时与我联系。

本月早些时候发表了这篇文章:

http://net.tutsplus.com/tutorials/php/why-youre-a-bad-php-programmer/

概括地说,如果您不编写注释,那么您就是一个糟糕的程序员。我个人的观点是,代码应具有描述性,并且除非注释不能自我描述,否则大多数情况下不需要注释。

在给出的例子中

// Get the extension off the image filename  
$pieces = explode('.', $image_name);  
$extension = array_pop($pieces);  

作者说此代码应该给出注释,我个人认为该代码应该是描述性的函数调用:

$extension = GetFileExtension($image_filename);

但是,在评论中实际上有人提出了这样的建议:

http://net.tutsplus.com/tutorials/php/why-youre-a-bad-php-programmer/comment-page-2/#comment-357130

作者回应说评论者是“其中的一个人”,即一个不好的程序员。

其他人对自我描述代码与评论代码有何看法?


1
@Péter-我确实看过那个,但是我更想在两者之间获得人们的个人见解,而不是将注释定义为是否具有代码气味。
菲尔

2
我发现那篇文章。非常侮辱
sevenseacat 2011年

@Karpie-我做了吗:(
Phill

3
“为什么您是不良的PHP程序员”答案:因为您使用PHP进行编程。
Thomas Eding

您首选的方法实际上是使用函数调用来注释您的代码。为什么不使用注释呢?函数应仅用于可重用的代码。我个人讨厌必须遵循以这种方式编写的其他人的代码,出于同样的原因,GOTO语句是邪恶的-它会创建意大利面条式代码。现代IDE可以更好地实现这一点,但是并不是所有的语言和程序员都可以使用这些IDE,并且它仍然令人迷惑。坦率地说,我同意您应该注释一下一目了然的代码部分,这只会使阅读代码变得更加快捷和容易。
dallin 2013年

Answers:


46

我更喜欢编写自我记录代码。清洁代码是对此的指南。

当然,这并不意味着一个人永远不要使用注释-它们有其作用,但是恕我直言,您应该谨慎使用它们。我的早期关于SO的答案更详细地解释了我对该主题的想法。

当然,正如@Niphra指出的那样,始终值得再次检查,以确保其他人确实可以理解我认为是干净的东西。但是,这也是一个实践问题。根据我的想法,我回到uni时只是因为对所有代码实体都使用了奇怪且有趣的名称,所以编写了一段神秘的代码。在我的老师退回我的一项作业之前,礼貌地指出他无法弄清楚哪个模块是主要的:-)这是一个很好的课程,因此我一直致力于写自那时以来更具可读性的代码。如今,我几乎没有收到队友的抱怨。


我喜欢那本书:)
Phill

我仍然发现评论对于表达所使用的策略以及被拒绝的人(有原因)很有价值。我同意微注释通常是无用的。
Matthieu M.

9
我从“干净代码”中最喜欢的一句话是,每个注释都无法用代码表达自己。
Doval

6
@Doval:一种有趣的哲学,至少是关于代码功能的注释。另一方面,一些最有用的注释可能是有关代码为何不执行某些操作的注释-我认为不应期望代码表达这一概念。
2015年

1
完全不同意。没有什么命名可以完全描述逻辑的目的。
Kolob峡谷

65

您不应该记录代码在做什么,但是应该记录它在做什么的原因。

没有多少命名技巧会透露其原因和原因,因此您必须添加注释来解释各种代码的用途。

所有其他注释都可以安全删除。


3
+1链接的文章中的示例说:“在我写的代码的目的没有立即显现出来的任何情况下,请发表评论。” -相信代码本身可以传达目的是很愚蠢的,因为机器本身没有目的的概念。反例:我们几乎在所有已编写的软件中都有错误,这是事实。如果计算机理解了目的(“ 为什么”),它只会拒绝执行我们未达到预期目的的任何事情,而不是尽职地重新创建导致错误的内容/时间/方式/位置。
David Meister 2015年

一个函数内部的所有代码都应该存在,以实现该函数的目的,即其功能,例如,将一些文本写入文件,该函数为“ writeTo(String text,File file)”。如果一些代码有不同的目的,例如检查先决条件,如核实该文件存在,它并不明显,那么也许是一个更好的主意比注释写一个新的功能,如“checkWritable(档案文件)”。代码质量不是教条式的清单,但是,如果它需要注释,那就不好了,“为什么”并不是需要它们的充分理由。鸡为什么过马路?
特里克斯2015年

2
@Trylks在某些情况下有效,但不适用于所有情况。想象一下这样的场景:for (int i = 0; i < length/2;i++) { //if length is odd, the middle element should stay in place。现在,从封闭函数的用途来看,这既不是显而易见的,也不是可以将其分解为自己的函数的。如果不加注释,那显然很糟糕。因此,您添加一条注释以阐明您的意图。
biziclop 2015年

2
@Trylks此外,即使我们以您的示例为例,您也可以将您的签出分解为一种方法并将其称为好方法。现在,您需要解释的是为什么您需要检查文件是否可写。:)
biziclop 2015年

38

我实际上不相信自我描述的代码。还有更可读的代码更少的可读的代码,根据不同的语言,你对它的了解(原作者),这个家伙读它,和代码函数的相关知识。但不,仍然...应简短说明。

什么是现在很清楚,我,我是在思考这一领域可能不会很清楚我在一年,当我思考的东西完全不同,需要再次使用这部分代码。

因此,注释您的代码。当然,并不是每一行(天哪,不是),但要在函数/子例程/模块或特别棘手的部分上方添加几行注释,并简要说明其作用。一两年后,您会感谢自己的。


去过也做过。“可读性较低的代码”应改进为可读性强的代码。语言知识并不是很重要:当您不熟悉语法时,应该首先学习它-坦率地说,这是成为专业人士的基础。试图通过添加注释来弥补糟糕的代码不是一个好主意。注释永远不要试图描述代码的作用。仅当您需要说出原因而不是说什么时,(一般)评论才是合理的。其他一切只是噪音和不必要的额外维护。
Powerslave '16

PS,我知道我有点“丧尸”了:)抱歉
Powerslave '16

为避免混淆,我正在谈论一种通常的情况,即当您不因业务需求(例如出色的性能或适合于受限存储)而被迫编写不良代码时。在其他(罕见)情况下,您别无选择,只能发表评论,这在一定程度上减轻了下一位受害者的负担。
Powerslave '16

19

幸运的是,这里讨论的两个阵营都在这里代表,并且都提到了赞成和反对的论点。

我相信两个阵营都有重叠的论据,并且实际上都同意其中的大多数观点,只是实现它们的方式略有不同。

重叠参数

  • 代码应可读。
  • 注释不应说出与代码完全相同的内容,而应在必要时提供进一步的见解。
  • 应该仔细考虑所有变量/函数名称,以便它们能很好地表示它们的作用。
  • 应避免重复代码。

现在,主要区别在于其中一些论点的重要性。

自描述代码

  • 注释可能会过时,因此请尽量减少使用它们,因为错误的注释比没有注释更糟糕。
  • 注释是代码的重复。可以用代码编写的所有内容都应该用代码编写。

更多评论

  • 注释比代码更具可读性。朴素的英语更能描述事物。

  • 普通代码通常会引起歧义,无论如何都必须对此进行注释。试图用代码描述这个,导致名字太长。此外,您将不断面对这些“额外”信息,而这些信息仅在您第一次遇到时才需要。

我相信两个阵营都有非常有效的论据,但是您不应该疯狂地追随一个阵营,仅因为它解决了一个问题。

为了说明这一点,在“ 清洁代码 ”一书中,代码被分解为许多较小的方法,这些方法仅被调用一次。创建方法的唯一原因是记录代码(并且简化了TDD)。这导致功能地狱。与原始代码相比,该代码的可读性较差,并且在重构时,并未考虑封装可重用的代码。

另一方面,您经常会在API的每个功能都被注释的地方看到它们,只是因为“注释很好”。应该被评论的东西仍然没有。


1
@Steven-“另一方面,您经常会在API的每个功能处看到注释,仅仅是因为'注释很好'。应该注释的东西却没有。” gh,真令人沮丧!
dss539 2014年

这是一个很好的答案!如果您不介意的话,一件事是关于评论的可读性:我个人不认为评论更具可读性。作为开发人员,我们通常会考虑源代码,尤其是在“编码模式”下。在那些时刻,普通人类语言的模糊通常更是分散注意力而不是帮助。
Powerslave '16

5

“对不起,但是你那个家伙。”

我不知道他为什么不喜欢评论:P

认真地讲,编码实在是一门艺术,以至于人们无法如实地做出如此全面的声明。有时您需要注释,有时需要更多更好的命名函数。通常都是。

将识字编程视为一种极端的风格。


是的 我的意思是,如果唐纳德·克努斯(Donald Knuth)认为您不仅需要评论,而且有时还需要其中的章节,那么在不同意之前,我至少要三思而后行。
2011年

3

简短,更好和正确的答案

编写良好的“自记录代码”就是您所需要的想法,这是一种反模式,并且即使在解释“为什么”的注释中出现例外时也应该失效。有一个神话,您总是可以为任何算法编写足够清晰的代码,以使任何程序员都可以浏览并获得它(或者它不需要重构或组织时间,而您没有)。更重要的是,认为自己编写清晰代码的程序员通常不会这样做。

注释更好的答案仅应用于解释“为什么”是注释应:

  • 解释“为什么”(当然)
  • 仅当代码很复杂或目的不明确并且不能或不值得进一步简化时,才在一行上解释“什么”
  • 解释代码块的“内容”,以加快理解速度并找到所需的内容

备份说明

人们错误地认为人们使用注释的唯一原因是解释一行代码的含义。事实是,注释代码的主要目的是使代码更快浏览您的代码并找到您想要的。当然,当我稍后返回代码或阅读别人的代码时,我可以阅读和理解编写良好的代码部分,但是阅读顶部的注释说该部分代码的作用是不是更快,更容易?如果不是我要的内容,请完全跳过它?如果您可以看一眼注释并理解整个功能,那么即使坐在那里,即使代码编写得很好,也为什么要坐在那里找出代码呢?这就是为什么我们对函数使用描述性名称-没有人说我不需要为函数使用描述性名称,因为有人可以浏览我编写清楚的代码来查看其功能。

例如,如果我正在查看其他人的功能,是否更容易逐行浏览代码以查看其功能,或者浏览整个功能中三个写得好的注释,以查看该功能的确切位置以及在哪里在做吗?

另一个反模式是过度使用函数来注释您的代码。命名函数是代码文档的重要组成部分,但有时程序员会将2-3行代码分开,这些代码永远不会在其他地方用于文档目的。为什么过度使用功能比过度使用注释更好?使用类似的功能与拥抱GOTO语句相同-它会创建意大利面条式的代码,可能会让人感到痛苦。

本质上,当您在企业环境中工作时,人们一直在共享代码,而人们却总是没有时间完善自己的代码,因此,一些好的注释可以节省大量的时间和挫败感。请记住,虽然您可能是一位能够以轻快的速度阅读代码的专家,但并非办公室中的每个人都可以。


我讨厌人们投票反对,甚至不发表评论。您甚至阅读了整个帖子吗?里面没有什么是简单而真实的?你不同意什么?我觉得人们对他们的想法或所读内容的看法是如此坚定,以至于他们甚至都没有考虑过,并且会立即投票反对任何不同意他们的观点。
dallin 2013年

3
我认为这是因为您完全拒绝并命名反模式,而这一点几乎被普遍认为是正确的。我当然认为你走得太远。通常,我无法想象您会像现在一样信任评论。如果您盲目地阅读注释而不是代码,那么注释可能是错误的并且过时了,并且您永远不会知道。这是使用函数作为文档的基础。我同意您不应在一处拥有太多功能,尽管解决方案绝对不是用注释代替它们。
Magus 2014年

@Magus感谢您的评论。我假设您是指我在谈论使用函数作为文档。重读一遍,我相信我的措辞很差,听起来好像我的意思是完全为此目的使用函数(而不是为此目的过度使用函数)是不好的。我已经整理了该段以阐明我的初衷。我对信任评论的想法是,如果评论过时,那通常是您对一段代码的评论太狭窄的信号-您在评论实现而不是意图。
dallin 2014年

but isn't it faster and easier to read the comment at the top saying what that section of code does and skip it altogether if it's not what I'm looking for它称为“方法/函数的名称”。如果您有一段没有名字但很长的代码块,以至于无法一目了然,那么问题可能就出在这里。
biziclop 2015年

1
@biziclop加班我开始相信该参数主要是语义,实际上,我们的代码看起来很相似。话虽这么说,假设代码块未在一个以上的地方使用,我看不出顶部带有简短描述性注释的代码块与顶部带有简短描述性功能名称的代码块之间的区别。最佳。它们是一样的,除了一个没有空格。它们可能是错误的,也可能是过时的。我真正看到的唯一区别是代码更易于注释,因为您不必将函数调用放在单独的位置。
达林2015年

2

嗯,您还必须记住一些对您而言显而易见或“自我记录”的东西,可能对其他人不是……也许是对某些功能了解不足的人。所以我对所有事情都发表评论。


1
能给我举个例子吗。我的意思是在最初的问题中给出示例。“ GetFileExtension”在注释“获取给定文件的文件扩展名”中有什么意义?该方法已经描述了注释中应包含的内容。如果该方法被命名为“ GetExtension”,则注释将有助于阐明该方法的作用,因为该方法没有解释自身。
菲尔

+1这是一个了解您的受众的问题。谁可能使用此功能?您如何帮助他们?您的帮助将有价值吗?是否值得花一点时间来添加评论?等等等等
PeterAllenWebb 2011年

2
@PeterAllenWebb:一点都没有指向该评论。这并不意味着该功能不应该被注释;这应该。“扩展名”是否包含点?返回值是"foo"多少?(null""?)为"foo."?传入null会导致异常,还是函数会返回某些内容(也许null)?
TJ Crowder

2

好的,带有自我记录代码的是在该函数中,您会发现:

$pieces = explode('.', $image_name);  
$extension = array_pop($pieces);  

因为只有两行,所以当您具有函数名称时,这很容易解释。当事情变得更加复杂时,您要么必须将每几行代码包装在具有描述性名称的函数中,要么在必要时使用注释。

我从来不明白为什么它应该是一个或/或问题,而不是和/和。是的,使您的代码尽可能多地自我记录,是的,在本来很难理解的部分添加一些注释。


是的 我认为必须“训练”您以为应该是一个或另一个,而不是两者都应。
PeterAllenWebb 2011年

2

注释和自记录的干净代码不同。代码是关于如何做事的。注释应涵盖“ 为什么”部分,无论您使用哪种语言,都无法用代码解释。另外,如果您的语言非常有限并且没有合同,没有静态规范甚至没有断言,则注释应涵盖代码的边界问题。


假设一切都是有原因的,应该对此发表评论吗?
JeffO 2011年

是的,正好。这就是为什么我认为注释代码的最佳方法是熟练的编程。
SK-logic

1

在这种情况下,很容易进行描述。但是我读了很多优秀的程序员编写的代码,他们认为他们的代码是自我记录的,对他们来说显而易见的事情确实让我感到困惑。

$ extension = GetFileExtension($ image_name);

回到您的例子,我可以给它发送一组图像名称,还是仅拍摄一张图像?它支持任何类型的文件,还是仅支持其中一些类型?它将为我保护字符串,还是我必须这样做?如果文件类型不存在,它会通知我吗?

当然,我对此进行了扩展。但是我记得一个程序员,他相信audio_bandwidth和video_bandwidth是自我记录的名称。原来音频必须以字节表示,视频必须以千字节表示。花了很多时间弄清楚那个。


5
您正在大量地拉伸那个。该函数获取文件扩展名。一无所有 它的名字表明是否需要一个数组(显然不是)。我想不出一种依靠文件类型来获取扩展名的方法。保护字符串是唯一可以合并的字符串,但是我从没想到会这样做,因为任何PHP开发人员都知道:所有用户输入都是可疑的,因此请在使用前对其进行保护。
马特·艾伦

2
@Matt:“显然不是”表示您不经常进行维护。显式地节省了以后的工作量(和RTFSource),并且还记录了原始作者的期望。无论是在注释中还是在长函数名中,都没有那么重要。
Donal Fellows,

也许这个例子在这个问题上是一个不好的例子,我已经有7年没有接触过PHP了,一直在做C#和一点点Java,所以我习惯了一个IDE来告诉我返回的类型或声明类型。
菲尔

1
@Donal Fellows:说文件,单数。这有什么歧义?这是完全明确的。
马特·艾伦

4
现在,来自4个人的7个答复讨论了单个函数调用的明显性或其他事实,这向我表明您需要使用注释。还强调了一个事实,即准确的命名本身就是一种艺术形式。
蚂蚁

1

一个不排除另一个。即使您的代码是自我注释的,但有时您可能仍需要定期注释以解释为什么您的自我注释代码会执行此操作。


我认为这属于我的观点,即“代码应具有描述性,除非代码不能自我描述,否则大多数情况下不需要注释”。如果编写的代码编写得不错,但不能完全说明代码的含义,则注释有助于使代码更具描述性。
菲尔

代码永远无法解释其目的。当您看到锤子时,您不知道它是做什么用的-是砸头骨还是仅仅锻造生铁。
SK-logic

1
@ SK-logic:public <T> void hit(T item);
Michael K

@Michael-完全是。用户如何弄清楚,哪些类别的物体可以并且应该被锤击?即使使用更好的类型系统,也不总是很清楚,开发人员实际上计划了哪些可能的用途。
SK-logic

1

我不同意该文章,并在某种程度上同意你的看法。如果您使用好的方法名称,好的变量名称和小的方法来完成一件事情,那么代码应该很简单。

只是不要变得聪明,因为聪明的代码是可怕的读取和维护。关键字:维护

我的意见是,评论应描述原因而不是原因。请记住,在这个假设的完美世界中,您的代码足够干净,可以轻松阅读,您不需要解释它在做什么,但是为什么选择这样做或那样做。

如果您使用的是源代码管理系统,则可以使用commit消息让所有人(和您自己)都知道在给定时间所做的事情,更重要的是为什么。


1

您可能希望避免编写注释,就像您希望避免使用任何文档一样。对于编程语言本身,每个人(几乎)都使用相同的词汇和语法进行操作。

当您的应用程序用于特定领域时,可能很难让所有参与其中的人都同意并/或建立通用词汇表。我们被教导要避免缩写和大量的行话,但我将其称为

息税折旧摊销前利润

并不是

权益折旧及摊销前的权益

如果您一个都不认识,则您可能不了解另一个。如果公司有一些不常见的实现方式,那么发表评论将对可能在该领域具有经验的下一个程序员有所帮助,但对这家特定公司没有帮助(这会使事情变得更加复杂。)。


1

我认为我们需要区分文档和代码的表达性。

在调试或检查代码时,您不是在读书。大多数时候,您只是想在方法之间跳转,并在脑海中快速建立联系,以基本了解运行时的状况。在此过程中,重要的不是代码的文档而是代码签名的表达能力,它们足够有意义,您可以立即识别它们并将其添加到自己的内部调用堆栈中。在这一点上,我们的大脑(至少,我的大脑是这样工作的;))倾向于将大的评论区视为噪音,而不是帮助。因此,单行注释,甚至更好,仅是自描述方法和对象名称就足够了。

如果您想“阅读特定类别或功能的书”,那么在单元测试中最好的选择是。设计良好的单元测试本质上是意图揭示,并且比最厚的注释块要多得多的文档(即说明性的,详细的),因为它们包含1 /对此代码应做的确切期望和2 /检查的能力这些期望与实际代码不符。通过测试的可靠性比任何评论都高100倍,因为它证明了它所断言的真实性。


1

有些代码只是不自我记录,需要理解和测试该代码的同伴的一些评论。我认为,我下面的内容不足以理解它。

//
// iterative version
//
Node* ReverseList( Node ** List ) 
{

  Node *temp1 = *List;
  Node * temp2 = NULL;
  Node * temp3 = NULL;

  while ( temp1 )
  {
    *List = temp1; //set the head to last node 
    temp2= temp1->pNext; // save the next ptr in temp2
    temp1->pNext = temp3; // change next to privous
    temp3 = temp1;
    temp1 = temp2;
  }

  return *List;
}

1

我通常喜欢编写自记录代码,并在注释不明确之处写注释,因为我认为大多数代码不会完全记录自身。


0

在大学里,我们被教导要用注释将英语的每一行代码重新改写(可能只是让我们养成理解代码实际作用的习惯,而不是仅仅复制/粘贴某些内容并希望获得最好的结果)。

就个人而言,我认为这会使您的代码杂乱无章,与仅仅是注释或代码相比,它的可读性较差。我是C#编码人员,我一直做的唯一注释是“三斜杠”注释块,这些注释块被解释回IntelliSense文档。如果我对做某事的特定方式感到特别内,,或者看起来特别晦涩难懂,我将给出进一步的解释,仅此而已。

IMO:自记录代码是为变量和方法名称赋予有意义的上下文名称的代码,以便它们描述其用途。


0

如果您多次重新访问了代码,但仍未找到一种方法来使意图清楚的人知道该域。重写函数。毕竟不超过10-20行。如果更长,无论如何都要重写该功能,这就是为什么它不可读的原因之一:)

在不太可能的情况下,仍然不清楚代码在做什么,并且您记得要向同行寻求帮助。好吧,我们都感谢您帮助不断发展的Linux,因为您编写的内核代码正确吗?如果没有冲洗,请从顶部重复:)

简而言之,不要写您在评论中的代码


0

查看Code Complete 2nd Edition,第128-129页。

抽象数据类型将使您免于这个难题。自我记录代码是必经之路。评论可能有用,但是

真实世界的实体,而不是底层的实现

您可以避免使用注释。

关于注释的一件事是您只编写了一次,但是在实现功能时看不到它们,只有在更改功能时才看到它们。

当IDE用Delphi 2009+或JavaDoc的工作方式对注释进行解释时,注释确实非常有用,但这更像是一种结构化标记语言,因此从某种意义上说,您正在编写文档,这非常聪明。


0

我坚信,代码本身并不能说明问题,因为您可能是世界上最好的程序员(Ada),但对所发生的事情一无所知,但是如果您可以说明原因并在短期内得到说明,您的代码如何执行其功能,将来会为您自己和他人提供帮助。


0

评论是必须的。因为编写代码时,您是在为当前的需求而编写,同时也为将来必须阅读代码,弄清楚wtf,您在做什么,为什么做以及如何对其进行修改的人们。

如果只是记住这一点,那么在编码/编程时?

我如何才能使它更容易理解和修改,以供将来正在使用此代码的编码人员使用,那么您会做得很好。失败了,您只是让其他人很难修改您的代码,并且别以为永远不会这样,那是罕见的...

在我的大部分工作中,我总是不得不修改其他人的代码,并且编写得最糟,文档很少。

因此,您认为代码文档本身就是一个习惯,只是没有进行尽职调查。

作为程序员,我们必须练习那些对缺乏经验的程序员似乎完全是自学的自律,但是必须有习惯,以避免我们对别人的代码所经历的所有可怕的经历。甚至是几个月后,再看看我们自己的代码。

查阅http://thedailywtf.com,他们有大量幽默而真实的故事,讲述了程序员没有尽职调查的情况。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.