我有一位同事坚持认为他的代码不需要评论,而是"自我记录".
我已经回顾了他的代码,虽然它比我见过其他代码生成的代码更清晰,但我仍然不同意自我编写代码是完整和有用的以及评论和记录的代码.
帮助我理解他的观点.
什么是自我记录代码
它真的可以取代评论和记录良好的代码
是否存在比记录良好和注释的代码更好的情况
是否存在代码无法在没有注释的情况下进行自我记录的示例
也许这只是我自己的局限,但我不知道它是如何成为一种好的做法.
这并不是一个争论 - 请不要提出为什么评论和记录良好的代码是高优先级的原因 - 有很多资源显示这一点,但它们并不能让我的同行相信.我相信我需要更充分地理解他的观点来说服他.如果必须,请开始一个新问题,但不要在此争论.
哇,快速反应!请阅读所有现有答案,并为答案提供评论,而不是添加新答案,除非您的答案与此处的其他答案完全不同.
此外,那些反对自我记录代码的人 - 这主要是为了帮助我理解自我记录代码福音传道者的观点(即积极方面).如果你不留下话题,我希望别人会贬低你.
好吧,因为这是关于注释和代码,让我们看看一些实际的代码.比较这个典型代码:
float a, b, c; a=9.81; b=5; c= .5*a*(b^2);
这个自我记录的代码,显示了正在做的事情:
const float gravitationalForce = 9.81; float timeInSeconds = 5; float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
然后是这个记录的代码,它更好地解释了为什么要这样做:
/* compute displacement with Newton's equation x = v?t + ½at² */ const float gravitationalForce = 9.81; float timeInSeconds = 5; float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
最终版本的代码作为文档,需要零注释:
float computeDisplacement(float timeInSeconds) {
const float gravitationalForce = 9.81;
float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
return displacement;
}
这是一个糟糕的评论风格的例子:
const float a = 9.81; //gravitational force float b = 5; //time in seconds float c = (1/2)*a*(b^2) //multiply the time and gravity together to get displacement.
在最后一个示例中,当变量应该被描述性地命名时使用注释,并且当我们可以清楚地看到操作是什么时,总结操作的结果.我希望任何一天都能有自己记录的第二个例子,也许这就是你的朋友在谈到自我记录的代码时所说的话.
我会说这取决于你在做什么的背景.对我来说,在这种情况下,自我记录的代码可能就足够了,但是详细说明背后的方法背后的方法(在这个例子中,方程式)也很有用.
在我看来,任何代码都应该是自我记录的.在良好的,自我记录的代码中,您不必解释每一行,因为每个标识符(变量,方法,类)都有一个清晰的语义名称.实际上有更多的评论实际上使得阅读代码更加困难(!),所以如果你的同事
为每个类,成员,类型和方法AND编写文档注释(Doxygen,JavaDoc,XML注释等)
清楚地评论代码中任何非自我记录的部分
为解释意图的每个代码块写一个注释,或者代码在更高的抽象级别上执行的操作(即查找大于10 MB的所有文件而不是遍历目录中的所有文件,测试文件大小是否大于10 MB,如果为真,则收益率返回)
在我看来,他的代码和文档很好.请注意,自记录代码并不能意味着应该没有意见,但只应该有任何不必要的评论.但是,通过阅读代码(包括注释和文档注释),可以立即了解代码的作用和原因.如果"自我记录"代码比注释代码需要更长的时间来理解,那么它实际上并不是自我记录的.
代码本身始终是对代码所做内容的最新解释,但在我看来,它很难解释意图,这是评论最重要的方面.如果它的正确写入,我们已经知道什么代码的功能,我们只需要知道为什么地球上它吧!
有人说过
1)只为难以理解的代码编写注释.
2)尽量不要编写难以理解的代码.
"自我记录"代码背后的想法是代码中的实际程序逻辑非常清楚,足以向任何阅读代码的人解释代码正在做什么但为什么要这样做.
在我看来,真正的自我记录代码的想法是一个神话.代码可以告诉你正在发生的事情背后的逻辑,但它无法解释为什么它以某种方式完成,特别是如果有多种方法来解决问题.仅仅因为这个原因,它永远不会取代评论良好的代码.
我认为问一个特定的代码行是否是自我记录是相关的,但最后如果你不理解一段代码的结构和功能,那么大多数时候评论都没有帮助.以amdfan的"正确评论"代码为例:
/* compute displacement with Newton's equation x = v0t + ½at^2 */ const float gravitationalForce = 9.81; float timeInSeconds = 5; float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2);
这段代码很好,但以下内容在大多数现代软件系统中都具有相同的信息,并明确承认使用牛顿计算是一种选择,如果其他一些物理范例更合适,可以改变它:
const float accelerationDueToGravity = 9.81; float timeInSeconds = 5; float displacement = NewtonianPhysics.CalculateDisplacement(accelerationDueToGravity, timeInSeconds);
根据我个人的经验,很少有"正常"的编码情况,你绝对需要评论.例如,您最终经常推出自己的算法?基本上所有其他事情都是构建系统的问题,以便编码人员能够理解使用中的结构以及驱使系统使用这些特定结构的选择.
我忘记了从哪里得到这个,但是:
程序中的每条评论都像是对读者的道歉."我很抱歉,我的代码非常透明,你无法通过查看来理解它".我们必须接受我们并不完美但努力做到完美,并在我们需要的时候道歉.
自我记录代码是"干"(不要重复自己)的一个很好的例子.不要在代码本身的注释中复制信息.
而不是解释变量的用途,重命名变量.
而不是解释代码的简短片段,将其提取到方法中并为其指定一个描述性名称(可能是评论文本的缩短版本).
而不是解释复杂测试的作用,将其提取到方法中并给它一个好名字.
等等.
在此之后,您最终得到的代码不需要那么多解释,它解释了自己,因此您应该删除仅在代码中重复信息的注释.
这并不意味着您根本没有任何评论,有些信息无法放入代码中,例如有关意图的信息("为什么").在理想情况下,代码和注释相互补充,每个都添加唯一的解释值而不会在另一个中复制信息.
自我记录代码是一种很好的做法,如果正确完成,可以轻松传达代码的含义,而无需阅读太多注释.特别是在团队中的每个人都很好理解域名的情况下.
话虽如此,评论对新来者或测试人员或生成文档/帮助文件非常有用.
自我记录代码+必要的评论将大大有助于团队中的人们.
首先,很高兴听到您的同事的代码实际上比您看到的其他代码更清晰.这意味着他可能不会使用"自我记录"作为懒得评论他的代码的借口.
自我记录代码是一种代码,它不需要自由文本注释,以便知情读者理解它在做什么.例如,这段代码是自我记录的:
print "Hello, World!"
这是这样的:
factorial n = product [1..n]
这是这样的:
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)
现在,这个"知情读者"的想法是非常主观和情境化的.如果您或其他任何人在跟踪您的同事代码时遇到问题,那么他应该重新评估他对知情读者的看法.必须假定对所使用的语言和库有一定程度的熟悉程度,以便调用代码自我记录.
我在撰写"自我记录代码"时看到的最好的论点是,它避免了自由文本评论的问题,因为它不符合编写的代码.最好的批评是,虽然代码可以自己描述它在做什么以及如何做,但它无法解释为什么某些事情以某种方式完成.
为了:
自我记录代码是向读者明确表达其意图的代码.
不是完全.评论总是有助于评论选择特定策略的原因.但是,解释代码部分正在做什么的注释表明代码不够自我记录并且可能使用一些重构.
评论谎言并且过时了.代码总是告诉更有可能说实话.
我从来没有见过,其中的情况下什么的代码不能进行足够清晰无意见; 然而,就像我之前所说的那样,有时必须/有帮助包括对原因的评论.
然而,重要的是要注意,真正的自我记录代码需要大量的自我和团队纪律.你必须学会更具声明性地编程,并且你必须非常谦虚并且避免使用"聪明"的代码来支持代码,这些代码很明显,似乎任何人都可以编写代码.
首先,请考虑以下代码段:
/** * Sets the value of foobar. * * @foobar is the new vaue of foobar. */ public void setFoobar(Object foobar) { this.foobar = foobar; }
在此示例中,每3行代码有5行注释.更糟糕的是 - 评论不会通过阅读代码添加任何您看不到的内容.如果你有10个像这样的方法,你可以得到'评论失明',而不会注意到一个偏离模式的方法.
如果当然,更好的版本应该是:
/** * The serialization of the foobar object is used to synchronize the qux task. * The default value is unique instance, override if needed. */ public void setFoobar(Object foobar) { this.foobar = foobar; }
不过,对于琐碎的代码,我更喜欢没有评论.在代码之外的单独文档中更好地解释了意图和整体组织.
当您阅读"自我记录代码"时,您会看到它正在做什么,但您无法总是猜测它为什么以这种特定方式进行.
有许多非编程约束,如业务逻辑,安全性,用户需求等.
当您进行维护时,那些backgorund信息变得非常重要.
只是我的一小撮盐......
您可能希望向您的同事指出的一件事是,无论他的代码如何自我记录,如果考虑并放弃其他替代方法,除非他用该信息评论代码,否则信息将丢失.有时同样重要的是要知道替代方案被考虑以及为什么决定反对,代码注释最有可能随着时间的推移而存在.
你听说过Donald Knuth的"WEB"项目来实现他的Literate Programming概念吗?它不仅仅是自我记录的代码; 它更像是可以作为代码编译和执行的文档.我不知道今天使用了多少.
区别在于"什么"和"如何".
你应该记录例程的"内容".
除非特殊情况(例如,参考特定的算法论文),否则您不应记录它是如何做到的.这应该是自我记录的.
在我工作的公司中,其中一位程序员将以下内容粘贴在她的显示器顶部.
"记录你的代码就像维护它的人是一个知道你住在哪里的同性恋疯子."