什么是自我logging代码,它可以代替有据可查的代码?
我有一个同事坚持认为他的代码不需要评论,这是“自我logging”。
我已经回顾了他的代码,虽然它比我见过其他代码更清晰,但我仍然不同意自编档代码是完整和有用的,以及评论和文档代码。
帮助我理解他的观点。
- 什么是自我logging代码
- 它可以真正代替评论和文档化的代码吗?
- 有没有比文件logging和评论代码更好的情况
- 有没有例子代码不可能自我logging没有评论
也许这只是我自己的限制,但我不认为这是一个好的做法。
这并不是一个争论 – 请不要提出为什么评论和文档化的代码是高优先级的原因 – 有很多资源显示这一点,但他们不相信我的同行。 我相信我需要更充分地理解他的观点,否则就说服他。 如果你必须开始一个新的问题,但不要在这里争论。
哇,快回复! 请阅读所有现有的答案,并提供答案的意见,而不是添加新的答案,除非你的答案真的是在这里大大不同于其他答案。
而且,那些反对自我logging代码的人 – 主要是帮助我理解自我logging代码传播者的观点(即积极方面)。 我希望如果你不停留在话题上,其他人会低估你。
在我看来,任何代码都应该是自我logging的。 在良好的,自我logging的代码中,你不必解释每一行,因为每个标识符(variables,方法,类)都有明确的语义名称。 有更多的意见比必要的实际上更难(!)阅读代码,所以如果你的同事
- 为每个类,成员,types和方法AND编写文档注释(Doxygen,JavaDoc,XML注释等)
- 清楚地评论代码中没有自我loggingAND的任何部分
- 为解释意图的每个代码块写一个注释,或者代码在更高的抽象级别上做什么(即find所有大于10MB的文件而不是循环遍历目录中的所有文件,testing文件大小是否大于10 MB,收益率如果为真 )
他的代码和文档很好,在我看来。 请注意,自行编写的代码并不意味着不应该有任何评论,但只应该没有不必要的评论。 但是,通过阅读代码(包括注释和文档注释)应该立即理解代码的作用和原因。 如果“自我logging”代码比注释代码花费更长时间才能理解,那么它并不是真正的自我logging。
那么,因为这是关于评论和代码,让我们看看一些实际的代码。 比较这个典型的代码:
float a, b, c; a=9.81; b=5; c= .5*a*(b^2);
对于这个自我logging的代码,它显示了正在做什么:
const float gravitationalForce = 9.81; float timeInSeconds = 5; float displacement = (1 / 2) * gravitationalForce * (timeInSeconds ^ 2)
然后到这个logging的代码,这更好地解释了为什么它正在完成:
/* 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)
而这个评论风格较差的例子是:
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.
在最后一个例子中,当variables应该被描述性地命名时,使用注释,当我们能够清楚地看到操作是什么时,总结操作的结果。 我宁愿自己logging下来的第二个例子,也许这就是你的朋友在说自我logging的代码时正在谈论的东西。
我会说这取决于你在做什么的背景。 对我来说,自编的代码在这种情况下可能就足够了,但是详细说明背后的方法(在这个例子中,方程)的评论也是有用的。
代码本身始终是对代码的最新解释,但在我看来,它很难解释意图 ,这是评论中最重要的方面。 如果编写得当,我们已经知道代码的作用了,我们只需要知道为什么它能做到这一点!
有人曾经说过
1)只写难以理解的代码的评论。
2)尽量不要写难以理解的代码。
“自我logging”代码背后的想法是,代码中的实际程序逻辑足够清楚,足以向任何读取代码的人解释不仅代码在做什么,而且为什么代码在做。
在我看来,真正的自我logging代码的想法是一个神话。 代码可以告诉你发生了什么事情的逻辑,但是它不能解释为什么它以某种方式完成,特别是如果有多种方法来解决问题的话。 仅仅因为这个原因,它永远不能取代评论好的代码。
我认为这是相关的问题是否一个特定的代码行是自我logging,但最后如果你不明白的代码片的结构和function,那么大部分时间的意见是不会帮助。 举例来说,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);
这个代码很好,但是在大多数现代软件系统中,下面的内容是同样的信息,并且明确地认识到,使用牛顿计算是一个可以改变的select,如果其他物理范例更合适的话:
const float accelerationDueToGravity = 9.81; float timeInSeconds = 5; float displacement = NewtonianPhysics.CalculateDisplacement(accelerationDueToGravity, timeInSeconds);
根据我个人的经验,绝对需要评论的“正常”编码情况非常less。 例如,你最终多久会滚动自己的algorithm? 基本上,其他的一切都是构build你的系统的问题,以便编码人员能够理解正在使用的结构以及驱使系统使用这些特定结构的select。
我忘了我从哪里得到这个,但是:
程序中的每一条评论都像对读者的道歉。 “对不起,我的代码是如此不透明,以至于看不懂它”。 我们必须承认,我们并不完美,但要努力做到完美,并在需要时道歉。
自我logging代码是一个很好的做法,如果做得好,可以很容易地传达代码的含义,而不用读太多的评论。 特别是在团队中每个人都很好理解域名的情况下。
话虽如此,评论可以为新来者或testing人员或生成文档/帮助文件非常有用。
自我logging的代码+必要的评论将帮助跨团队的人很长的路要走。
首先,很高兴听到你的同事的代码实际上比你所看到的其他代码更清晰。 这意味着他可能不会使用“自我logging”作为懒惰评论他的代码的借口。
自我logging代码是不需要自由文本评论的代码,让知情的读者理解它在做什么。 例如,这段代码是自我logging的:
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)
现在,这个“知情读者”的想法是非常主观和情景化的。 如果您或其他人在遵守您的同事代码时遇到困难,那么他会很好地重新评估他对知情读者的想法。 为了调用代码自我logging,必须假定对所使用的语言和库有一定的熟悉程度。
我所见过的编写“自我logging代码”的最好的论据是,它避免了自由文本注释不符合代码的问题。 最好的批评是,虽然代码可以描述自己在做什么以及如何做,但是它不能解释为什么某些事情正在以某种方式完成。
自我logging代码是“干”(不要重复自己)的一个很好的例子。 不要在注释中重复信息,或者可能在注释中注释。
而不是解释一个variables用于什么,重命名variables。
而不是解释一小段代码的作用,把它提取到一个方法中,并给它一个描述性的名字(也许是你的评论文本的缩短版本)。
而不是解释一个复杂的testing做什么,提取到一个方法也给它一个好名字。
等等。
在这之后,你最终得到的代码不需要太多的解释,它自己解释,所以你应该删除仅仅重复代码中的信息的注释。
这并不意味着你没有任何意见,有一些信息,你不能放在代码,如关于意图的信息(“为什么”)。 在理想的情况下,代码和评论相互补充,每一个都添加唯一的解释值,而不会在另一个中重复信息。
为了:
- 自我logging代码是清晰地向读者expression其意图的代码。
- 不是完全。 评论总是有助于评论为什么select一个特定的战略。 然而,解释一段代码所做的注释代表的是不足以自我logging的代码,并可能使用一些重构。
- 评论谎言,并成为过时。 Code
总是告诉更可能说实话。 - 我从来没有见过这样的情况,即没有评论的代码就不能被清晰地expression出来。 不过,正如我刚才所说,有时候有必要/有助于对此进行评论。
然而,需要注意的是,真正的自我logging代码需要很多自律和团队纪律。 你必须学会更多的声明性编程,而且你必须非常谦虚,避免使用“聪明”的代码来支持那些显而易见的代码,好像任何人都可以编写代码。
首先,请考虑下面的代码片段:
/** * 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; }
不过,对于微不足道的代码,我更喜欢没有评论。 这个意图和整个组织在代码之外的一个单独的文档中得到了更好的解释。
当你阅读一个“自我logging的代码”时,你会发现它正在做什么,但是你不能总是猜测为什么这样做。
有非常多的非编程约束,如业务逻辑,安全性,用户需求等。
当你做维护时,那些backgorund信息变得非常重要。
只是我的一小撮盐…
你可能希望向你的同事指出的一件事情是,无论他的代码如何自我logging,如果考虑和丢弃了其他替代方法,那么除非他用这些信息对代码进行评论,否则信息将会丢失。 有时候,知道另一个select是被考虑的,为什么决定不了,代码评论最有可能在一段时间内生存下来。
你有没有听说过Donald Knuth的“WEB”项目来实现他的Literate Programming概念? 这不仅仅是自我logging的代码, 它更像是可以作为代码编译和执行的文档。 我不知道今天用了多less。
区别在于“什么”和“如何”。
- 你应该logging一个例程“做什么”。
- 除非特殊情况,否则不应logging“如何”执行此操作(例如,请参阅特定algorithm文件)。 这应该是自我logging的。
在我工作的一个程序员的公司里,有一个人坚持到她的显示器的顶端。
“像维护它的人那样logging你的代码是一个知道你住在哪里的同性恋疯子”。
代码是自我logging的观点使我疯狂。 一个特定的代码行或者一个子algorithm可能确实是自我logging的,但是它在更大的图片中的目的根本不是。
我在一两个月前就非常沮丧,写了一篇博客文章来描述我的观点。 张贴在这里 。
自我logging代码通常使用与代码正在执行的variables名称相匹配,以便很容易理解正在发生的事情
但是,这种“自我logging的代码”永远不会取代评论。 有时候代码太复杂了,自我logging的代码是不够的,特别是在可维护性方面。
我曾经有一个教授坚信这个理论,实际上我记得他说的最好的事情是“评论是为了sissies”
首先,我们所有人都感到吃惊,但这是有道理的。
但是,情况是,尽pipe你可能能够理解代码中发生了什么,但是那些经验不足的人可能会跟在后面,不理解正在发生的事情。 这是评论变得重要的时候。 我知道很多次,我们不相信他们是重要的,但很less有评论是不必要的。
令人惊讶的是没有人带来“ 文学编程 ”,这是由TeX公司的Donald E. Knuth和“计算机编程艺术”于1981年开发的技术。
前提很简单:因为代码必须被人理解,而且编译器只能抛出注释,为什么不给每个人所需的东西 – 对代码的意图进行全面的文本描述,而不受程序devise语言的要求限制,为人类读者和纯编码的编译器。
Literate编程工具通过为文档提供特殊的标记来告诉工具哪些部分应该是源文件,什么是文本。 该程序稍后将源代码部分从文档中剥离出来并组装代码文件。
我在网上find了一个例子: http : //moonflare.com/code/select/select.nw或HTML版本http://moonflare.com/code/select/select.html
如果你能在图书馆findKnuth的书(Donald E. Knuth,Literate Programming,Stanford,California:The Language for Study and Information,1992,CSLI Lecture Notes,no.27),你应该阅读它。
这是自我logging的代码,完成推理和所有。 即使是一个很好的文档,其他的只是写得很好的评论:-)
我想提供一个更多的观点,许多有效的答案:
什么是源代码? 什么是编程语言?
机器不需要源代码。 他们很高兴运行组装。 编程语言对我们有利。 我们不想写汇编。 我们需要了解我们正在写什么。 编程是关于编写代码的。
你应该能够阅读你写的东西吗?
源代码不是用人类语言编写的。 已经尝试过(例如FORTRAN),但并不完全成功。
源代码不能有歧义。 这就是为什么我们不得不比文本更多的结构。 文本只与上下文一起工作,在我们使用文本时我们认为是理所当然的。 源代码中的上下文总是显式的。 在C#中思考“使用”。
大多数编程语言都有冗余,这样当编译器不一致时,编译器可以抓住我们。 其他语言使用更多的推断,并试图消除冗余。
计算机不需要types名称,方法名称和variables名称。 他们被我们用来参考。 编译器不理解语义,这是我们使用的。
编程语言是人与机器之间的语言桥梁。 它必须是可写的,对我们来说是可读的。 次要的要求是,它应该是我们可读的。 如果我们在允许和擅长构造代码的情况下擅长语义,那么即使对于我们来说,源代码也应该易于阅读。 最好的代码不需要评论。
但是每一个项目都蕴含着复杂性,你总是要决定把复杂性放在哪里,哪些骆驼吞下去。 这些是使用评论的地方。
自我logging代码是一个简单的退出问题,随着时间的推移代码,评论和文件分歧。 而且这是编写明确代码的一个训练因素(如果你对自己严格的话)。
对我而言,这是我试图遵循的规则:
- 代码应该尽可能简单明了地阅读。
- 评论应该给出我所做的devise决策的理由,比如:为什么我使用这个algorithm,或者代码有什么限制,比如:当…时不工作(这应该在代码中的合同/断言中处理)(通常在function/程序中)。
- 文档应列出使用(调用converntions),副作用,可能的返回值。 它可以使用工具(如jDoc或xmlDoc)从代码中提取。 因此它通常在函数/过程之外,但接近它所描述的代码。
这意味着所有三种logging代码的方法都相互靠近,因此在代码更改时更有可能被更改,但是它们所expression的内容并不重叠。
所谓的自我logging代码的真正问题在于它传达了实际的function。 尽pipe一些评论可能会帮助别人更好地理解代码(例如,algorithm步骤等),但是这在一定程度上是多余的,我怀疑你会说服你的同行。
然而,在文档中真正重要的是从代码中直接看不到的东西:潜在的意图,假设,影响,限制等等。
能够快速地确定一个代码是否比X更容易,而不是确定一个代码不会执行Y.他必须loggingY.
你可以向他展示一个看起来不错的代码,但是实际上并没有涵盖input的所有基础,例如,看看他是否find它。
我认为,自我logging代码是一个很好的替代评论。 如果您需要注释以解释代码是如何或为什么是这样的,那么您应该修改一个函数或variables名称,使其更具说明性。 可以由编码人员来决定是否通过注释弥补缺陷或者重新命名一些variables和函数以及重构代码。
它不能真正代替你的文档,因为文档是你给别人解释如何使用你的系统,而不是它如何做的事情。
编辑:我(也可能是其他人)应该可以提供一个数字信号处理(DSP)应用程序应该得到很好的评论。 这主要是因为DSP应用程序本质上是2循环馈送数值的数组,并添加/乘以/等所说的值…改变程序,你改变其中一个数组的值…需要一些评论说什么你正在这样做;)
在编写math代码的时候,我有时会发现写长篇散文的评论,解释math,代码使用的符号约定,以及它们如何结合在一起是有用的。 在这里,我们正在谈论数百行文档。
我试图尽可能地将自己的代码作为自己的文档,但是当我几个月后回来处理它的时候,我确实需要阅读这个解释,以避免出现这种情况。
当然,对于大多数情况来说,这种极端的措施当然不是必要的。 我认为这个故事的寓意是:不同的代码需要不同数量的文档。 有些代码可以写的很清楚,不需要注释 – 所以写清楚,不要在那里使用注释!
但是很多代码确实需要注释,所以尽可能清楚地写下来,然后使用尽可能多的注释。
我认为 – 和许多人一样 – 为了真正自我logging,代码需要performance出某种forms的意图。 但是我很惊讶没有人提到BDD – 行为驱动开发 。 这个想法的一部分是,你有自动化testing(代码)来解释你的代码的意图,这是很难做出明显的表示。
良好的领域build模 +好名字(变种,方法,类) +代码示例(来自用例的unit testing) =自我logging软件
我的观点写在这篇文章中:
一个单一的提示来logging你的代码。
摘抄:
为什么不重新组织你的逻辑,使其不言而喻,而不是写很多评论来解释你的程序的微妙行为? 而不是logging一个方法正在做什么,为什么不select一个明确的名称为该方法? 为什么不只是抛出一个NotImplementedException()?而不是标记代码来表示未完成的工作。 Instead of worrying whether your comments sound polite enough to your boss, your colleagues or anyone reading the code, why not just stop worrying by not writing them at all?
The clearer your code is, the easier it is to maintain it, to extend it, to work on it on future editions. The less ordorous is your code, the less need there is to comment it. The more the comments, the higher the maintanence cost.
A couple of reasons why extra comments in addition to the code might be clearer:
- The code you're looking at was generated automatically, and hence any edits to the code might be clobbered the next time the project is compiled
- A less-than-straightforward implementation was traded off for a performance gain (unrolling a loop, creating a lookup table for an expensive calculation, etc.)
Its going to be all in what the team values in its documentation. I would suggest that documenting why/intent instead of how is important and this isn't always captured in self documenting code. get/set no these are obvious – but calculation, retrieval etc something of the why should be expressed.
Also be aware of difference in your team if you are comming from different nationalities. Differences in diction can creap into the naming of methods:
BisectionSearch
BinarySearch
BinaryChop
These three methods contributed from developers trained on 3 different continents do the same thing. Only by reading the comments that described the algorithm were we able to identify the duplication in our library.
For me reading code that needs comments is like reading text in the language I do not know. I see statement and I do not understand what it does or why – and I have to look at comments. I read a phrase and I need to look in dictionary to understand what it means.
It is usually easy to write code that self-documents what it does. To tell you why it does so comments are more suitable, but even here code can be better. If you understand your system on every level of abstraction, you should try organizing you code like
public Result whatYouWantToDo(){ howYouDoItStep1(); howYouDoItStep2(); return resultOfWhatYouHavDone; }
Where method name reflects your intent and method body explains how you achieve your goal. You anyway can not tell entire book in its title, so main abstractions of your system still have to be documented, as well as complex algorithms, non-trivial method contracts and artifacts.
If the code that your colleague produc is really self-documented – lucky you and him. If you think that your colleagues code needs comments – it needs. Just open the most non-trivial place in it, read it once and see if you understood everything or not. If the code is self-documented – then you should. If not – ask your colleague a question about it, after he gives you an answer ask why that answer was not documented in comments or code beforehand. He can claim that code is self-document for such smart person as him, but he anyway has to respect other team members – if your tasks require understanding of his code and his code does not explain to you everything you need to understand – it needs comments.