我一再被告知在编程时不要重复自己。我遇到过一个场景,我想知道是编写两个函数,每个函数用于特定目的,还是一个函数来处理这两个目的。我不确定什么是“最佳实践”方法:
所讨论的所有功能都涉及允许某人将列表项的等级更改为一个位置,加上或减去一个。所有项目及其等级都保存在MySQL数据库表中。)
两个功能一个功能可以将项目的等级提高一个,另一个功能可以将等级向下移动一个。
一个函数我可以通过添加一个额外的“direction”参数并使用if语句来决定是向上还是向下移动项目,从而将相同的函数写入一个函数。
就性能而言,编写两个函数并让它们共享负载会更好吗?如果相同的功能收到大量请求,是否存在“功能过载”这样的事情?
我认为它不相关,但我使用的是ColdFusion,而我的附加“方向”参数将是一个包含在语句中的字符串,该语句检查它的值以确定要采取的操作。
答案 0 :(得分:5)
不,DRY并不一定意味着您的应用程序会表现更好。它使更容易编写性能良好的应用程序(因为优化只需要在一个地方完成),它几乎肯定会使您的应用程序更容易维护。但理论上,非DRY应用程序的性能与DRY应用程序一样好。
选择再写一个通用函数或两个更具体的函数不一定与DRY相同。您必须平衡两个函数中有多少代码相同,以及与通用函数的额外复杂性有多大差异。在我看来,这是一个边缘情况,但是当我完成它时,我已经在一个通用功能中完成了它。
编程中有一个术语“函数重载”,但它意味着与你的建议有很大不同。通过过于频繁地调用函数,没有太多“强调”函数的风险。
答案 1 :(得分:3)
通常恰恰相反;更多的重用,更多的抽象层意味着在微观层面上你的程序正在做更多的工作(除非你之间有一个非常聪明的编译器或优化器。)消除DRY的简单例子,现在通过现代编译器和运行时通常没有意义循环展开和函数内联。
然而,限制抽象所带来的微小收益通常主要是通过编写由可重用部分组成的组织良好的程序而获得的可读性和灵活性的巨大增长。
答案 2 :(得分:2)
不,不一定。
优势在于您需要更改旧代码的维护部分。如果在多个地方重复使用相同的代码,那么您不会修复所有这些地方的风险很高。
DRY通过避免多份副本来避免这种情况。注意,但是需要添加条件并不意味着你服从DRY,因为那时你正在合并两个不同的函数。
通过多次调用它们来关注“重载”函数。它不起作用。 CPU不关心代码是在一个还是两个函数中,只要设计是纯粹的并且您的代码尊重并行程序的语义(这本身就具有挑战性)。
答案 3 :(得分:2)
DRY用于创建更小且更易维护的代码。
从性能角度来看,在大多数情况下,差异几乎不存在。在编译语言中,编译器将优化可能存在的任何性能差异。
答案 4 :(得分:2)
DRY(不要重复自己)原则并非真正旨在最大限度地提高性能,但坚持它可以使其更容易优化以获得更好的性能。 DRY旨在简化代码并使其更轻薄,更易于维护。
在实践中,这可能意味着性能的提升,因为一旦你找到了一种最佳的做事方式,你就可以在你编写它的地方和它所使用的任何地方实现它,从中获益。
答案 5 :(得分:2)
与许多其他程序化方法(或首字母缩略词,模式或其他类似方法)一样,您的方法应该是务实的方法,因为没有一种方法总是等于“性能更好”的应用程序。在我的头脑中,我会说你可能会以空间的形式实现名义上的节省,但可能不会达到你注意到它或产生影响的程度。
然而,最终,DRY的主要目的是为了表现更好的开发者,而不是表现更好的应用程序。如果需要进行优化,那么只有一个地方可以做到,而不是必须记住你自己“重复”的所有地方:)
答案 6 :(得分:2)
DRY是关于编写正确的代码,无论是现在还是将来。
如果您的代码被破坏,它的速度无关紧要。
答案 7 :(得分:1)
实际上(在大多数情况下,取决于编译器)是具有多个函数的性能开销,因为函数调用本身成本。使用DRY方法的主要原因是为了更容易维护和理解您的应用程序。
<强>更新强> 更新以回答对此答案的评论。这是对一般问题的回答,因为它远远不是DRY自动导致性能更好的应用程序,尽管这里提到的成本确实很少。
如果在Debug/Window/Disassembly
窗口打开时运行以下命令:
class Program
{
static void Main(string[] args)
{
Do();
Do2();
Console.ReadKey();
}
private static void Do()
{
int a = 10;
a = a + 10;
a = a * 10;
Console.WriteLine(a);
}
private static void Do2()
{
int a = 10;
Add(ref a);
Mult(ref a);
Console.WriteLine(a);
}
private static void Add(ref int a)
{
a = a + 10;
}
private static void Mult(ref int a)
{
a = a * 10;
}
}
在反汇编窗口中,您将看到应用程序必须通过的实际语句来调用Do
和Do2
。 Do2
模拟 DRY程序。如果单步执行汇编代码,您会注意到每个函数顶部的部分类似于以下部分:
00000000 push ebp
00000001 mov ebp,esp
00000003 push eax
00000004 mov dword ptr [ebp-4],ecx
00000007 mov eax,dword ptr ds:[71D81468h]
0000000d cmp dword ptr [eax],0
00000010 je 00000017
00000012 call FFFFDD68
00000017 nop
这是开销的一部分,只在Do()中发生一次。除此之外,总会有一个操作将控制流引导到内存中的不同位置。
话虽如此,我绝对会建议在几乎所有情况下使用DRY。