违反DRY原则总是不好吗?

Rys*_*gan 44 refactoring design-patterns

我一直在讨论关于DRY(不要重复自己)的原则,也称为DIE(Duplication Is Evil),并且有投票,任何简单的代码重复总是邪恶的.我想听听你对以下几点的看法:

  1. 不确定的未来.让我们说,我们在两个地方有相同的代码.关键是,这两个地方只有偶然的内涵.有可能,它们将来会有所不同,因为它们的上下文和语义不同.从这些地方抽象并不便宜,如果其中一个地方发生变化,从抽象中解开将更加昂贵.
  2. 可读性.存在涉及若干变量或步骤的复杂计算.在代码的其他地方还有另一个代码,它们有一些相同的部分.问题是,如果我们取出公共部分,计算的可读性会降低,创建抽象将很难给它一个描述性的名称.更糟糕的是,如果算法的某些部分将来会像第1点那样发生变化.

上述情况是否是放弃抽象过程的好理由,只留下重复的代码以支持未来变更的风险或只是可读性?

pat*_*cek 31

这些都是违反DRY的完全正当理由.我应该添加第三个:性能.它很少是一个大问题,但它可以产生影响,抽象可能会降低速度.

实际上,我将添加第四个:浪费时间并通过更改可能已经正常工作的代码库的两个(或更多)部分来引入新的错误.如果你没有这些东西,是否值得花费如何抽象这些东西的成本,它可能不会在未来节省任何或很多时间?

通常情况下,重复的代码并不理想,但肯定有令人信服的理由允许它,可能还包括OP和我自己建议的其他原因.

  • 是"绝对!" 回答问题标题或问题内容的最后一行?您需要考虑添加其他内容,但这并不能完全说明您的立场.你的最后一句是"有时!",而不是"绝对!". (2认同)

Ali*_*Ali 16

是的,在不使可读性显着恶化的情况下,某些代码重复很难被分解出来.在这种情况下,我留下TODO评论作为提醒,有一些重复,但在写作时,似乎更好地保留它.

通常情况是你在第一点写的东西,重复分歧,不再重复.复制也是设计问题的标志,但后来才变得清晰.

长话短说:尽量避免重复; 如果复制是非常难以分解的,并且在撰写时无害,请留下评论作为提醒.


另见每个程序员应该知道的97件事:

页.14.小心Udi Dahan的分享

系统的两个完全不同的部分以相同的方式执行某些逻辑的事实意味着比我想象的要少.直到我拿出那些共享代码库,这些部分并没有相互依赖.每个人都可以独立发展.每个人都可以改变其逻辑,以适应系统不断变化的商业环境的需求.这四行类似代码是偶然的 - 时间异常,巧合.

在这种情况下,他在系统的两个部分之间创建了更好的独立性.解决方案基本上是重复的.


Vau*_*ato 15

让我们试着理解为什么DRY很重要,然后我们就能理解破坏规则的合理性:

应该使用DRY来避免两个代码在概念上做同样工作的情况,因此每当你在一个地方更改代码时,你必须在另一个地方更改代码.如果相同的逻辑位于两个不同的位置,那么您必须始终记住在两个位置更改逻辑,这可能非常容易出错.这可以适用于任何规模.它可以是正在复制的整个应用程序,也可以是单个常量值.也可能没有任何重复的代码,它可能只是一个重复的原则.你必须问"如果我要在一个地方做出改变,我是否一定需要在其他地方进行同等的改变?".如果答案为"是",则代码违反DRY.

想象一下,你的程序中有这样的一行:

cost = price + price*0.10 // account for sales tax
Run Code Online (Sandbox Code Playgroud)

在你的程序的其他地方,你有一个类似的路线:

x = base_price*1.1; // account for sales tax
Run Code Online (Sandbox Code Playgroud)

如果销售税发生变化,您将需要更改这两行.这里几乎没有重复的代码,但事实上,如果你在一个地方进行更改,它需要在另一个地方进行更改,这就是使代码不干的原因.更重要的是,可能很难意识到你必须在两个地方进行改变.也许你的单元测试会抓住它,但也许不会,所以摆脱重复很重要.也许您会将销售税的价值计入一个可以在多个地方使用的单独常量:

cost = price + price*sales_tax;
x = base_price*(1.0+sales_tax);
Run Code Online (Sandbox Code Playgroud)

或者可能创建一个函数来抽象它:

cost = costWithTax(price);
x = costWithTax(base_price);
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,它很可能是值得的麻烦.

或者,您可能拥有看起来非常相似但未违反DRY的代码:

x = base_price * 1.1; // add 10% markup for premium service
Run Code Online (Sandbox Code Playgroud)

如果您要更改计算销售税的方式,您不希望更改该行代码,因此实际上并不重复任何逻辑.

还有一些情况需要在多个地方进行相同的更改是可以的.例如,也许你有这样的代码:

a0 = f(0);
a1 = f(1);
Run Code Online (Sandbox Code Playgroud)

此代码在某些方面不是DRY.例如,如果要更改函数名称,f则必须更改两个位置.您可以通过创建一个小循环并转换a为数组来使代码更干.但是,这种特殊的重复并不是什么大问题.首先,这两个变化非常接近,所以不小心改变一个而不改变另一个.其次,如果您使用的是编译语言,那么编译器很可能无论如何都会遇到问题.如果您不是编译语言,那么希望您的单元测试能够捕获它.

有很多很好的理由让你的代码干掉,但还有很多很好的理由.

  • +1.也许更重要的是,你的代码中可能有一行代码,如`x = base_price*1.1; //为高级服务添加10%的标记.将该代码与您的销售税计算合并将是一个_bad_想法,即使他们当前正好相同的事情. (5认同)

Dan*_*tín 11

工程就是权衡,所以没有明确的建议或设计模式对每个问题都有效.有些决策比其他决策更难以支持(代码重复就是其中之一),但如果重复代码的优点超过了它在你的情况下的缺点,那么就去做吧.