当使用支持嵌套函数(例如Python和D)的语言处理复杂算法时,我经常编写大型函数(因为算法很复杂),但是通过使用嵌套函数来构造复杂代码来减轻这种情况。即使大型功能(超过100行)仍通过使用嵌套功能在内部进行了结构合理,仍然被认为是邪恶的?
编辑:对于那些不熟悉Python或D的人,这些语言中的嵌套函数还允许访问外部函数范围。在D中,此访问权限允许外部范围内的变量发生突变。在Python中,它仅允许读取。在D中,可以通过声明显式禁用对嵌套函数中的外部范围的访问static
。
当使用支持嵌套函数(例如Python和D)的语言处理复杂算法时,我经常编写大型函数(因为算法很复杂),但是通过使用嵌套函数来构造复杂代码来减轻这种情况。即使大型功能(超过100行)仍通过使用嵌套功能在内部进行了结构合理,仍然被认为是邪恶的?
编辑:对于那些不熟悉Python或D的人,这些语言中的嵌套函数还允许访问外部函数范围。在D中,此访问权限允许外部范围内的变量发生突变。在Python中,它仅允许读取。在D中,可以通过声明显式禁用对嵌套函数中的外部范围的访问static
。
Answers:
有人认为,短函数比长函数更容易出错。
Card and Glass(1990)指出,设计复杂性实际上涉及两个方面:每个组件内部的复杂性和组件之间关系的复杂性。
就个人而言,我发现注释良好的直线代码(将其分解成多个在其他地方从未使用过的功能)更容易遵循(尤其是当您不是最初编写它的人时)。但这确实取决于情况。
我认为主要的收获是,当您分割一块代码时,您正在用一种复杂性交换另一种。中间某处可能有一个最佳位置。
理想情况下,无需滚动即可查看整个功能。有时,这是不可能的。但是,如果您可以将其分解成碎片,那么它将使阅读代码变得更加容易。
我知道,只要按上一页或下一页或移到代码的不同部分,我就只记得前一页的7 +/- 2件事。不幸的是,其中一些位置将在阅读新代码时使用。
我总是喜欢考虑我的短期记忆,例如计算机的寄存器(CISC,而不是RISC)。如果您在同一页面上拥有全部功能,则可以转到缓存以从程序的另一部分获取所需的信息。如果整个功能不能放在一个页面上,则相当于在每次操作后始终将任何内存推入磁盘。
为什么要使用嵌套函数,而不是普通的外部函数?
即使仅在您以前使用的大型函数中使用了外部函数,它仍然使整个混乱更容易阅读:
DoThing(int x){
x += ;1
int y = FirstThing(x);
x = SecondThing(x, y);
while(spoo > fleem){
x = ThirdThing(x+y);
}
}
FirstThing(int x){...}
SecondThing(int x, int y){...}
ThirdThing(int z){...}
目前我还没有这本书(引用),但是根据Code Complete的说法,根据他的研究,函数长度的“最佳点”约为25至50行代码。
有时候可以使用长函数:
不能使用长函数的时候:
最重要的是,可维护性应该是列表中的最高优先事项之一。如果另一个开发人员看不到您的代码并在不到5秒钟的时间内“了解”代码的运行情况,那么您的代码就无法提供足够的“元数据”来说明其运行情况。其他开发人员应该仅通过查看所选IDE中的对象浏览器就能知道您的类正在做什么,而不必阅读100多行代码。
较小的功能具有以下优点:
清单继续.....
答案取决于它,但是您可能应该将其变成一堂课。
我不喜欢大多数嵌套函数。Lambda属于该类别,但除非它们的字符数超过30至40,否则通常不会标记我。
根本原因是它成为具有内部语义递归的高度局部密集的函数,这意味着我很难动脑筋,并且将某些内容推送到一个不会使代码空间混乱的辅助函数会更容易。
我认为功能应该发挥作用。做其他事情就是其他功能。因此,如果您有200行功能“做事情”,而且一切顺利,那就可以了。
较长的“直线”功能可以是一种明确的方法,用于指定总是以特定顺序出现的冗长的步骤序列。
但是,正如其他人提到的那样,这种形式倾向于具有局部复杂性,其中总体流程不太明显。将局部复杂性放入嵌套函数(即:在long函数中的其他位置定义,可能在顶部或底部)可以使主线流程恢复清晰。
第二个重要的考虑因素是控制仅在长函数的局部范围内使用的变量范围。需要保持警惕,以避免在一个代码段中引入一个变量而在其他地方无意中引用了该变量(例如,在编辑循环之后),因为这种错误不会显示为编译或运行时错误。
在某些语言中,此问题很容易避免:可以将本地代码段包装在其自己的块中,例如使用“ {...}”,在其中,任何新引入的变量仅对该块可见。某些语言(例如Python)缺少此功能,在这种情况下,本地函数对于强制使用较小的作用域区域很有用。
不可以,多页功能是不可取的,并且不应通过代码审查。 我也曾经写过长函数,但是在阅读了Martin Fowler的Refactoring之后,我停了下来。长函数难以正确编写,难以理解且难以测试。我从来没有见过即使只有50行的功能,如果将其拆分为一组较小的功能,也不会更容易理解和测试。在多页函数中,几乎可以肯定应该排除整个类。很难具体说明。也许您应该将长期功能之一发布到Code Review上,然后有人(也许是我)可以向您展示如何改进它。
当我用python编程时,我想在编写函数后退后一步,并问自己是否遵守“ Python禅”(在python解释器中键入“ import this”):
美丽胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套更好。
稀疏胜于密集。
可读性很重要。
特殊情况还不足以打破规则。
尽管实用性胜过纯度。
错误绝不能默默传递。
除非明确地保持沉默。
面对模棱两可的想法,拒绝猜测的诱惑。
应该有一种-最好只有一种-显而易见的方法。
尽管除非您是荷兰人,否则一开始这种方式可能并不明显。
现在总比没有好。
虽然永远不会总比正确要好现在。
如果实现难以解释,那是个坏主意。
如果实现易于解释,则可能是个好主意。
命名空间是一个很棒的主意-让我们做更多这些吧!