我的一个同事认为,任何使用代码内注释(即,不是javadoc样式方法或类注释)都是一种代码气味。你怎么看?
我的一个同事认为,任何使用代码内注释(即,不是javadoc样式方法或类注释)都是一种代码气味。你怎么看?
Answers:
仅当注释描述了代码在做什么时。
如果我想知道某个方法或块中发生了什么,我将阅读代码。无论如何,我希望所有从事给定项目的开发人员至少对开发语言足够熟悉,以阅读所编写的内容并了解其功能。
在某些极端优化的情况下,您可能使用的技术会使某人难以跟踪您的代码在做什么。在这些情况下,注释可以并且应该不仅用于解释为什么要进行这种优化,还可以用于注释代码在做什么。一个好的经验法则是让其他(或多个其他人)熟悉实现语言和项目的人查看您的代码-如果他们既不理解为什么以及如何理解,那么您应该同时评论为什么和如何如何。
但是,代码中尚不清楚的是您为什么要做某事。如果您采取的方法对于其他人来说可能并不明显,那么您应该在注释中说明为什么您做出了自己的决定。我怀疑您可能甚至没有意识到需要注释,直到进行代码审查之类的事情之后,人们才想知道为什么您用X而不是Y-您可以在代码中捕获所有看它的人的答案。在将来。
不过,最重要的是在更改代码时更改注释。如果更改算法,请确保更新注释,以说明为什么使用算法X而不是Y。过时的注释是更大的代码味道。
此刻听到的消息特别令人不快,我这个周末花了一些时间,看了一个非常有名,非常干净,没有注释的代码,该代码实现了一种研究算法(实际上并未发布)。我对此非常熟悉,坐在我旁边的那个人是发明家,而代码是几年前由其他人编写的。我们几乎不能跟随它。
很显然,您的同事经验不足。
评论应说明原因,而不是原因。
How
通常,使用重构可以更好地处理类型注释。就我个人而言,我通常避免评论赞成重构。
之前:
# convert to cents
a = x * 100
# avg cents per customer
avg = a / n
# add to list
avgs < avg
t += 1
后:
total_cents = total * 100
average_per_customer = total_cents / customer_count
track_average(average_per_customer)
我宣布你的同事是异端!我的异端燃烧靴子在哪里?
强迫性注释是不好的,而且维护起来很头疼,注释不能替代名称明确的方法,类,变量等。但是有时说明为什么要这样做,对于必须维护代码的可怜白痴来说可能是非常有价值的在六个月之内-尤其是当那个可怜的白痴成为你的时候。
我正在处理的代码中的一些实际注释:
// If this happens, somebody's been screwing around with the database definitions and
// has removed the restriction that a given alarm may have only one entry in the
// notifications table. Bad maintenance programmer! Bad! No biscuit!
// If an alert is active on our side but inactive on theirs, that might mean
// they closed the alert. (Or that we just haven't told them about it yet.) The
// logic comes later; for now, we'll just compile it in a list.
// If we know for a fact that an alarm isn't getting through, we're going to whine pretty
// aggressively about it until it gets fixed.
理想情况下,代码应该被很好地编码,以使其具有自动解释性。在现实世界中,我们知道有时也需要注释非常高质量的代码。
您绝对应该避免的是“注释代码冗余”(注释不会在代码中添加任何内容):
i++; // Increment i by 1
然后,如果有一个好的(并且保持/保持一致的)代码设计和文档,那么注释就没什么用处了。
但在某些情况下,注释可以帮助提高代码的可读性:
while( foo )
{
if( dummy )
{
}
else // !dummy
{
}
} // end while( foo )
别忘了您还必须维护和保持评论同步...过时或错误的评论会带来极大的痛苦!而且,通常来说,注释过多可能是不良编程的征兆。
} //end while
只是意味着while循环的开始距离很远,您甚至看不到它,也没有暗示您正在查看的代码处于循环中。对于代码的结构注释,应该优先考虑使用重重构。
} // end while
评论可以挽救生命。
将方法或过程分类定义为“代码气味”是“狂热气味”。该术语正在成为新的“被认为有害”。
请记住,所有这些事情都应该作为指南。
许多其他答案对于何时发表评论提供了很好的建议。
我个人很少使用评论。说明非显而易见的过程的目的,并将偶尔的死亡威胁留给可能正在考虑自己进行更改并需要数周调整的任何人。
重构所有内容,直到幼儿园可以理解为止,这可能无法有效地利用您的时间,并且可能无法获得更简洁的版本。
注释不会影响运行时间,因此唯一需要考虑的负面问题就是维护。
在某些情况下,良好的命名,重构等功能都无法替代注释。看看这个真实的例子(语言是Groovy):
response.contentType="text/html"
render '{"success":true}'
看起来很奇怪,不是吗?可能是复制粘贴错误?哭了一个错误修正?
现在与注释相同:
// DO NOT TOUCH THE FOLLOWING TWO LINES; the ExtJS UploadForm requires it exactly like that!!!
response.contentType="text/html" // must be text/html so the browser renders the response within the invisible iframe, where ExtJS can access it
render '{"success":true}' // ExtJS expects that, otherwise it will call the failure handler instead of the succss handler
这里的主要问题是术语“代码气味”的含义。
许多人(我认为包括您在内)都了解代码气味是接近错误的东西,或者至少是需要修复的东西。也许您认为它是“反模式”的同义词。
这不是这个词的意思!
代码气味隐喻源自Wards Wiki,他们强调:
请注意,CodeSmell暗示可能出了问题,而不是确定性。完美的习惯用法可能被认为是CodeSmell,因为它经常被滥用,或者因为在大多数情况下有一个更简单的替代方法可以起作用。调用CodeSmell并不是攻击。这只是一个迹象表明必须仔细观察。
因此,注释是代码气味的意思是什么:它意味着当您看到注释时,您应该停下来思考:“嗯,我感觉到有一些可以改进的提示”。也许您可以重命名变量,执行“提取方法”重构-也许注释实际上是最好的解决方案。
编辑:我只是绊倒在这两篇文章,这比我更好地解释了:
我认为规则很简单:想象一个完全陌生的人看到您的代码。您可能会在5年后对自己的代码陌生。尽量减少为了解此陌生人的代码而付出的精力。
拥有正确评论的一个好主意是从编写评论开始。
// This function will do something with the parameters,
// the parameters should be good according to some rules.
myFunction(parameters)
{
// It will do some things to get started.
// It will do more with the stuff.
// It will end doing things with the stuff.
}
这使您可以轻松提取方法,甚至摆脱注释,
只需让代码告诉这些事情即可!看看如何以非常好的方式重写(剪切/粘贴):
// This function will do something with the parameters,
// the parameters should be good according to some rules.
myfunction(parameters)
{
var someThing = initializedWithSomething;
doSomethingWith(someThing);
doMoreWith(someThing);
endDoingThingsWith(someThing);
return someThing;
}
// This function will do some things to get started,
// the parameters should be good according to some rules.
doSomethingWith(parameters)
{
parameters.manipulateInSomeWay();
... etc ...
}
... etc ...
对于无法分离的事物,请不要提取方法并在注释下键入代码。
我认为这是将注释保持在最低限度的一种有用方法,对每一行进行注释真的没有用...仅记录一行有关魔术值初始化或有意义的地方。
如果参数使用过多,则它们应该是您的类中的私有成员。
// Dear me in the future. Please, resolve this problem.
要么
// You think this code was written by somebody else.
// No, it wasn't. You ([some name]) did it.
代码注释绝对不是“代码气味”。这种信念通常来自以下事实:评论可能会陈旧(过时)并且难以维护。但是,拥有良好的注释可以解释为什么代码以某种方式做某件事对于维护很重要(通常很重要)。
好的注释可以使您更容易理解代码在做什么,更重要的是,为什么要以特定的方式进行代码。注释应供程序员阅读,并且应清晰准确。难以理解或不正确的评论比根本没有评论要好得多。
在代码中添加清晰准确的注释意味着您不必依靠内存来理解代码部分的“内容”和“原因”。这一点在以后查看该代码时非常重要,否则其他人必须查看您的代码。因为注释已成为代码文本内容的一部分,所以除了清晰书写外,它们还应遵循良好的书写原则。
要写一个好的评论,您应该尽力记录代码的目的(为什么而不是如何),并尽可能清楚地说明代码背后的原因和逻辑。理想情况下,注释应在编写代码的同时编写。如果您等待,您可能不会回去添加它们。
Sams在24小时内自学Visual C#2010,第348-349页。
如果已以某种特定方式编写了代码,以避免出现库(第三方库或编译器随附的库)中存在的问题,则可以对其进行注释。
例如,注释需要在将来版本中更改的代码,或者在使用库的新版本时,或者从PHP4传递到PHP5时,也可以使用注释。
即使是写得最好的书,也很可能会有引言和章节标题。记录良好的代码中的注释对于描述高级概念以及解释代码的组织方式仍然很有用。
我不同意写注释来解释代码的想法不好。这完全忽略了代码存在错误的事实。这可能是清楚的代码是什么做不评论。不太清楚代码应该做什么。如果没有评论,您如何知道结果是否错误或使用不正确?
注释应说明代码的意图,以便在出现错误时阅读注释+代码的人有机会找到它。
通常,在编写代码之前,我通常会先编写内联注释。这样一来,我就可以清楚地知道我要编写的代码,并减少了在算法中不会真正知道自己在做什么的迷路。
我将自己回答一个问题。您可以在下面未注释的代码中找到该错误吗?
tl; dr:下一个维护您代码的人可能不像您那样敬虔。
[org 0x7c00]
main:
mov ah, 0x0e
mov bx, string
call strreverse
call print
stop:
jmp $
strreverse:
pusha
mov dx, bx
mov cx, 0
strreverse_push:
mov al, [bx]
cmp al, 0
je strreverse_pop
push ax
add bx, 1
add cx, 1
jmp strreverse_push
strreverse_pop:
mov bx, dx
strreverse_pop_loop:
cmp cx, 0
je strreverse_end
pop ax
mov [bx], al
sub cx, 1
add bx, 1
jmp strreverse_pop_loop
strreverse_end:
popa
ret
print:
pusha
print_loop:
mov al, [bx]
cmp al, 1
je print_end
int 0x10
add bx, 1
jmp print_loop
print_end:
popa
ret
string:
db 'Boot up', 0
times 510 -( $ - $$ ) db 0
dw 0xaa55
您必须在代码和注释之间保持平衡...通常,我会尝试添加一些可恢复代码块的注释。不是因为我无法理解代码(嗯,也是),而是因为我可以更快地阅读自己的代码,并在发生重大事件的地方找到特定的部分。
无论如何,我个人的标准是“有疑问时发表评论”。我更喜欢有一条冗余线,而不是一条我不会理解的完全神秘的线。一段时间后,我总是可以删除代码审查中的注释(我通常这样做)
另外,注释添加“ caveats”非常有用,例如“注意!如果输入的格式不是ASCII,则必须更改此代码!”
我认为代码注释起步很糟糕。我不知道这些日子,但是当我在学校第一次学习编程时,我得到了“编写一个将数字以十到十的数字打印在单独的行中的程序。请确保对代码进行注释”的性质。如果您不添加注释,则会被打下标记,因为注释您的代码是一件好事。
但是,对于这样一个微不足道的过程,有什么要说的呢?所以你最终写经典
i++; // add one to the "i" counter.
只是为了获得一个不错的成绩,并且,如果您一点都不喜欢,就会立即对代码注释发表意见。
代码注释不是一件好事。这是有时必要的事情,而在最上面的答案中的托马斯·欧文斯(Thomas Owens)很好地解释了必要的情况。但是,这些情况很少出现在家庭作业类型的作业中。
在许多方面,当不能在编程语言的有效部分中明确说出需要说的内容时,添加注释应被视为最后的选择。尽管对象命名可能会过时,但是各种人为和计算机缺乏反馈的机制使忘记维护注释变得容易,因此注释比活动代码更快地过时。因此,在可能的情况下,始终应该选择更改代码使其更清晰,而不是使用注释注释不清晰的代码。
每个程序员都知道,由于工作量,调试或遇到的疯狂行为,我们最终都会变得疯狂。
“做这个!” 您的项目经理说。
您回答:“无法完成。”
他们说:“那么我们会找其他人去做。”
您说:“好的,也许可以做到。”
然后花接下来的X天数..周..月..试图找出答案。在整个过程中,您将尝试失败,并且尝试失败。我们都这样做。真正的答案是有两种类型的程序员,一种是注释的,另一种是不注释的。
1)这样做的目的是通过编写文档以备将来参考,注释掉不起作用的失败例程(发现有用的例程后气味并没有删除它们),从而使自己的工作变得更容易,或者通过注释分解代码格式化到希望使它有点更容易阅读和理解。说真的,我不能怪他们。但最后,它们会折断,然后您会得到以下信息:
// dammit this code sucks! swear! curse! i hate it! i am going to write something here to vent my anger!!!!
2)那些没有装扮成超级英雄或住在山洞里的人。他们只是鲁re地无视他人,而对代码或以后可能具有的含义则不太在意。
现在不要误会我..自记录变量和函数可以完全避免这种情况.. 相信我,您永远无法进行足够的代码清理。但简单的事实是,只要保留备份,就可以始终删除注释。
我必须同意你的同事。我总是说,如果我的评论我的代码,这意味着我担心,我将无法弄清楚自己的代码在未来。这是一个坏兆头。
我向代码中添加注释的唯一其他原因是要调用似乎没有意义的内容。
这些评论通常采用以下形式:
//xxx what the heck is this doing??
要么
// removed in version 2.0, but back for 2.1, now I'm taking out again
向您的同事介绍Literate编程技术。
不,注释不是代码的味道,它们只是可以滥用的工具。
好的评论示例:
//我认为这是厘米。需要进一步调查!
//这是做X的聪明方法
//此处的列表保证为非空
Assert(list.IsEmpty)
呢?
Assert(!list.isEmpty())
与第三条注释中的约定并不完全相同,只是行为(例如,如果参数为空,则抛出IllegalArgumentException),该行为必须像任何其他程序逻辑一样进行单元测试。注意注释的细微差别,该注释指出何时可以使用该方法,但是如果不满足前提条件,则不指定任何行为。比评论更好的是执行编译时合同。但是,这超出了我的回答范围;)
Assert
s,因为它们描述了永远都不会发生的事情,即使公共API接收到无效的参数也是如此。