函数长度会影响程序员的生产率吗?如果是这样,为避免生产率损失,最大的最大行数是多少?
由于这是一个高度自以为是的主题,请备份索赔并提供一些数据。
函数长度会影响程序员的生产率吗?如果是这样,为避免生产率损失,最大的最大行数是多少?
由于这是一个高度自以为是的主题,请备份索赔并提供一些数据。
Answers:
自从我1970年开始使用这种疯狂的球拍以来,我已经确切地看到一个模块确实需要超过一个打印页面(约60行)。我见过很多更长的模块。
为此,我编写了更长的模块,但是它们通常是写为大型开关语句的大型有限状态机。
问题的一部分似乎是这些天没有教程序员如何对事物进行模块化。
最大化垂直空间浪费的编码标准也似乎是问题的一部分。(我尚未见过一位读过Gerald Weinberg的“ 计算机编程心理学 ”的软件经理。Weinberg指出,多项研究表明,程序员的理解力基本上仅限于程序员在任何给定的瞬间所看到的内容。程序员必须滚动或翻页,他们的理解力才会大大下降:他们必须记住并抽象化。)
我仍然坚信,FORTH在许多有据可查的程序员工作效率上的提高是由于源代码的FORTH“块”系统所致:模块硬限制为绝对最多16行64个字符。您可以无限分解,但是在任何情况下都不能编写17行例程。
取决于您使用的语言,但总的来说(根据我的个人喜好):
如果更多,那么我需要稍后再进行修改。
但实际上,当您需要交付某些东西时,它的大小可以是任意大小,并且在将它们吐出来的那一刻变得更加有意义,这有时使某些人在装运之前进行审核变得更加容易。(但稍后仍会重新讨论)。
(最近,我的团队在我们的代码库上运行了一个程序:我们发现类中有197个方法,另一个类中只有3个方法,但其中一个是600行。可爱的游戏:这2种弊端有何危害?)
现在,要获得一个更禅宗的答案...通常,引用一两个伟人被认为是一种好习惯(TM),因此,这里是:
作为对此的补充,您的函数应具有清晰的名称,以说明其意图。关于注释,我通常不在函数内部进行注释:
每个功能顶部的注释块(需要说明)就足够了。如果您的函数很小,并且函数名称足够明确,那么您只需要说出要实现的目标以及原因。我仅对某些语言的字段使用内联注释,或者对于意图不明确的违反25-35行规则的函数,在块开头使用内联注释。当发生特殊情况时,我在代码内使用块注释(例如,在您不需要或不想做任何事情的catch块中,应添加注释以说明原因)。
有关更多信息,请阅读我对样式的评论和注释代码的建议
tt
这些函数来生成这些函数,但有时您会遇到一个长屁股函数(或一个长屁股函数),而该函数实际上并没有做任何有意义的事情,因此这并不是真正的问题。
Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);
,当函数很明显时,它们都是完全相同的。(请注意,这只是一个例子;这种功能会不时弹出)
我认为,每个功能都应尽可能小。每个功能只能做一件事,并且做得很好。这并不能真正回答最大长度问题,但是更多的是我对函数长度的感觉。
用鲍伯叔叔的话来说,“直到提取不再提取为止。提取直到下降为止。”
只要它需要做它需要做的事情,就不再需要。
我认为这是一个权衡。如果您有很多短方法,那么调试它们通常比长方法要难。如果您必须在编辑器中跳出20或30个不同的时间来跟踪一个方法调用,那么很难将其全都牢记在心。同时,如果有一种写得很好的清晰方法,即使它是100行,通常也更容易掌握。
真正的问题是为什么项目应该采用不同的方法,并且上面给出的答案是代码的重用。如果您不重复使用(或不知道)代码,则可以将其保留在一个易于遵循的巨型方法中,然后在需要重复使用时,将需要重新使用的部分分开即可。使用较小的方法。
实际上,好的方法设计的一部分是制作功能一致的方法(本质上说,它们只能做一件事)。方法的长度无关紧要。如果一个函数做一个定义明确的事情,并且有1,000行,那么它是一个好方法。如果一个函数执行3或4件事并且只有15行,那么这是一个不好的方法...
如果可以一次看到整个功能,我发现跟踪自己的工作更容易。因此,这就是我更喜欢编写函数的方式:
我写函数的时间很少超过该时间。其中大多数是巨大的C / C ++开关语句。
问题应该是一个函数应该执行多少操作。通常,很少需要100行来完成“一件”事情。再次取决于您查看代码的级别:哈希密码是一回事吗?还是散列和保存密码是一回事?
我会说,首先将密码保存为一项功能。当您觉得哈希不同时,您便重构了代码。我绝对不是专家程序员,但是恕我直言,函数的整体思想从小开始,就是您的函数越原子化,代码重用的机会就越高,而不必在一个以上的地方进行相同的更改等
我已经看到运行1000行以上的SQL 存储过程。存储过程的行数是否也少于50?我不知道,但是它使阅读代码变得很糟糕。您不仅需要不断向上和向下滚动,还需要给几行代码起一个名称,例如“ thisvalidation1”,“ this update in database”等,这是程序员应该完成的工作。
从圈复杂度(维基百科):
一段源代码的循环复杂性是通过源代码的线性独立路径数的计数。
我建议您以一种方法将该数字保持在10以下。如果达到10,那么该重新构造了。
有一些工具可以评估您的代码并为您提供圈复杂度数字。
您应该努力将这些工具集成到构建管道中。
不要从字面上追逐方法的大小,而是尝试着看它的复杂性和责任。如果它承担的责任不止一个,那么重构是一个好主意。如果其圈复杂性增加,那么可能是时候进行重构了。
我敢肯定,还有其他工具可以为您提供类似的反馈,但是我还没有机会对此进行研究。
通常,我尝试将方法/功能保持在1680x1050显示器的屏幕上。如果不合适,请使用辅助方法/函数将任务打包。
它有助于提高屏幕和纸张的可读性。
我对任何事物都没有强加限制,因为某些函数实现的算法本质上是复杂的,任何使它们更短的尝试都会使新的较短函数之间的交互变得如此复杂,以至于最终结果不会降低简单性。我也不相信函数只能做“一件事情”的想法是一个很好的指导,因为在较高抽象级别上的“一件事情”在较低层次上可能是“很多事情”。
对我来说,如果函数的长度立即导致对DRY的细微违反,则该函数肯定会太长,而将部分函数提取到新函数或类中可以解决此问题。如果不是这种情况,函数可能会太长,但是可以轻松提取函数或类,这会使代码更具模块化,从而在面对可预见的变更时很有用。
足够短,可以正确优化
方法应该简短到可以做一件事。原因很简单:因此可以适当地优化代码。
在像Java或C#这样的JIT语言中,简单的方法很重要,这样JIT编译器才能快速生成代码。更长,更复杂的方法自然需要更多的JIT时间。而且,JIT编译器仅提供少数优化,只有最简单的方法才能从中受益。甚至在Bill Wagner的Effective C#中也提到了这一事实。
在较低级的语言(例如C或C ++)中,拥有短方法(大约十几行)也很重要,因为这样可以最大程度地减少将局部变量存储在RAM中而不是在寄存器中的需求。(又称为“寄存器溢出”。)但是请注意,在这种不受管理的情况下,每个函数调用的相对成本可能会很高。
即使使用像Ruby或Python这样的动态语言,使用简短的方法也有助于编译器的优化。在动态语言中,功能越“动态”,优化就越困难。例如,使用X并可以返回Int,Float或String的long方法可能比三个分别返回单个类型的单独方法的执行速度慢得多。这是因为,如果编译器确切地知道函数将返回哪种类型,它也可以优化函数调用站点。(例如,不检查类型转换。)
这在很大程度上取决于代码中的内容。
我看过一千行的例程,没有问题。这是一个巨大的switch语句,没有选项超过十二行,并且任何选项中唯一的控制结构是单个循环。如今,它本来是用对象编写的,但那不是当时的选择。
我还在前面的交换机中查看120条线。案件不超过3行-一名后卫,一名任务和一名休息。它正在解析文本文件,没有对象。任何其他选择都很难阅读。
我的一般规则是,功能应适合屏幕。我发现只有三种情况倾向于违反此规定:
1)调度功能。在过去,这些很常见,但是如今大多数都被对象继承所取代。不过,对象仅在程序内部运行,因此,在处理从其他位置到达的数据时,您仍然会偶尔看到调度功能。
2)执行一整套步骤以实现目标的功能,而这些步骤缺少很好的细分。您最终得到的功能只是简单地按顺序调用一长串其他功能。
3)像#2一样,但各个步骤是如此之小,以至于它们只是内联而不是单独调用。