让我们试着理解为什么 DRY 很重要,然后我们就可以理解在哪里打破规则是合理的:
应该使用 DRY 来避免两段代码在概念上做一些相同的工作的情况,所以每当你在一个地方更改代码时,你必须在另一个地方更改代码。如果相同的逻辑位于两个不同的地方,那么您必须始终记住更改两个地方的逻辑,这很容易出错。这可以适用于任何规模。它可以是被复制的整个应用程序,也可以是单个常量值。也可能根本没有任何重复的代码,它可能只是一个重复的原理。您必须问“如果我要在一个地方进行更改,我是否一定需要在其他地方进行等效更改?”。如果答案是“是”,那么代码违反了 DRY。
想象一下,你的程序中有这样一行:
cost = price + price*0.10 // account for sales tax
在你的程序的其他地方,你有类似的一行:
x = base_price*1.1; // account for sales tax
如果销售税发生变化,您将需要更改这两条线。这里几乎没有重复的代码,但是如果您在一个地方进行更改,则需要在另一个地方进行更改,这使得代码不是 DRY。更重要的是,您可能很难意识到您必须在两个地方进行更改。也许你的单元测试会捕捉到它,但也许不会,所以摆脱重复很重要。也许您会将销售税的价值分解为一个可以在多个地方使用的单独常量:
cost = price + price*sales_tax;
x = base_price*(1.0+sales_tax);
或者可能创建一个函数来进一步抽象它:
cost = costWithTax(price);
x = costWithTax(base_price);
不管怎样,这很可能是值得的。
或者,您可能有看起来非常相似但不违反 DRY 的代码:
x = base_price * 1.1; // add 10% markup for premium service
如果您要更改计算销售税的方式,您不会想要更改那行代码,因此它实际上并没有重复任何逻辑。
在某些情况下,必须在多个地方进行相同的更改是可以的。例如,也许你有这样的代码:
a0 = f(0);
a1 = f(1);
此代码在某些方面并非 DRY。例如,如果要更改 function 的名称,f
则必须更改两个位置。您也许可以通过创建一个小循环并a
变成一个数组来使代码更加干燥。但是,这种特殊的重复并不是什么大问题。首先,这两个更改非常接近,因此不太可能意外更改一个而不更改另一个。其次,如果您使用的是编译语言,那么编译器很可能无论如何都会发现问题。如果您不是使用编译语言,那么希望您的单元测试能够捕捉到它。
让你的代码 DRY 有很多很好的理由,但也有很多很好的理由不这样做。