违反DRY原则


10

我确信这个反模式在某处有个名字。但是,我对反模式文献还不了解。

请考虑以下情形:

or0是类中的成员函数。不管好坏,它在很大程度上取决于类成员变量。程序员A随之出现,并且需要一些功能,例如or0而不是调用or0,程序员A复制并重命名整个类。我猜她不会打电话,or0因为正如我所说,它在很大程度上取决于成员变量的功能。或者,也许她是一个初级程序员,不知道如何从其他代码中调用它。因此,现在我们有了or0c0(c为副本)。对于这种方法,我不能完全责怪程序员A -我们都在紧迫的期限内完成工作,并且我们乱砍代码以完成工作。

一些程序员or0对此进行了维护,使其成为当前版本orNc0现在是版本cN。不幸的是,维护类包含的大多数程序员or0似乎完全不知道-这c0是DRY原则的智慧中我能想到的最有力的论据之一。并且可能还已经独立维护了中的代码c。看来无论哪种方式,or0c0维持相互独立的。而且,喜悦和幸福是错误的发生,cN而不会发生orN

所以我有几个问题:

1.)此反模式有名称吗?我经常看到这种情况发生,我很难相信这不是一种命名的反模式。

2.)我可以看到一些替代方法:

a。)修复orN一个参数,该参数指定了所需的所有成员变量的值。然后修改cN以调用orN所有传入的所需参数。

b。)尝试手动将修补程序从移植orNcN。(请记住,我不想这样做,但这是现实的可能性。)

c。)再一次复制orNcN--yuck,但是出于完整性考虑,我列出了它。

d。)尝试找出cN损坏的地方,然后独立于对其进行维修orN

从长远来看,替代a似乎是最好的解决方案,但我怀疑客户会允许我实施它。从来没有时间或金钱来解决问题,而是总是花费时间和金钱来解决同一问题40或50次,对吗?

有人可以建议我可能没有考虑过的其他方法吗?


“这个反模式有名字吗?” “违反干燥”反模式?:)恕我直言,你几乎自己回答了。
史蒂文·杰里斯

也许称之为“干违反者”?
FrustratedWithFormsDesigner

2
我称呼它为:干颠簸。
AJC

5
复制和粘贴编码?
tylermac 2011年

这被称为“太多的厨师宠坏了肉汤”的模式。
布朗

Answers:


18
  1. 它只是所谓的重复代码 -我不知道这个有什么花哨的名字。长期后果与您所描述的一样,甚至更糟。

  2. 当然,如果可能的话,消除重复是理想的选择。这可能会花费很多时间(在我们的旧项目中,最近的一个案例是,我在类层次结构中的20多个子类中重复了几种方法,其中许多方法多年来在进化上逐渐发展出自己的细微差异/扩展。我花了大约1.5年的时间,通过连续的单元测试编写和重构来消除所有重复,但是坚持不懈是值得的)。

    在这种情况下,即使您决定开始消除重复,您仍可能需要一个或多个其他选项作为临时修复程序。但是,哪种更好取决于很多因素,如果没有更多背景信息,我们只是在猜测。

    从长远来看,许多小的改进可以带来很大的不同。您也不一定需要客户的明确批准-每次您触摸上述类以修复错误或实现功能时,都需要进行一些重构,这可能会随着时间的推移而走很长一段路。只需在任务估计中包括一些额外的重构时间即可。就像标准维护一样,可以长期保持软件的健康。


3
+1,不仅是因为提到重复的代码,而且还因为代码重构过程中的巨大努力。:D
Andreas Johansson

9

有对WET(我们喜欢打字)模式的引用,但我不知道它是否是标准名称。

...软件许可是DRY(请勿重复自己)做法的一个明显例外-软件许可代码应尽可能与WET(我们喜欢打字-与DRY相反)相同。

如果将许可逻辑与其他问题分开放在一个地方,则可以很轻松地修改软件以完全禁用许可。最好在整个应用程序中多次重复许可逻辑,最好在整个软件执行一次中多次执行许可逻辑。

例如,我们建议您在安装过程中,应用程序启动过程中以及每当访问重要功能时执行许可检查。在启动过程中不要检查许可证,并且不要传递该值;相反,实际上在每个区域中复制许可检查。这很不方便,但是比发布易于破解的产品要好得多...


3
WET可能意味着:两次编写所有内容
StuperUser 2011年

1
@StuperUser我昨天发现在dailywtf上...
jeremy-george

3

如果我对您的理解得当,那么这堂课太多了。这就产生了不遵循单一责任原则的方法。导致需要四处移动的代码,而其他部分则遗漏了。这也可能会创建很多成员。

您还应该查看可访问性修饰符。确保那些公共的东西,实际上应该是公共的。消费者不需要了解每个小成员...使用封装。

这需要重构。看来代码是在没有前期设计的情况下编写的。查看测试驱动开发。编写代码应如何调用而不是调用已实现的代码。查看TDD中有关如何完成任务的一些提示。


3

如果您要复制代码,则会带来双重维护或更具体的技术负担:代码重复

通常,这是通过重构解决的。更具体地说,您将所有调用重定向到具有通用代码的新函数(或新类中的新方法)。最简单的开始方法是删除所有复制的代码并查看中断,然后通过将调用重定向到通用代码来解决。

让您的客户同意重构代码可能很困难,因为很难说服非技术人员来解决技术债务。因此,下次您提供时间估算值时,只需包含重构到您的估算值所需的时间即可。在大多数情况下,客户都认为您在进行修复时正在清理代码。


1

听起来像意大利面条代码对我来说。最好的解决方法是重构/重写。

源代码的一种贬义性术语,具有复杂且纠结的控制结构,尤其是使用许多GOTO,异常,线程或其他“非结构化”分支结构的术语。之所以这样命名,是因为程序流程从概念上讲就像一碗意大利面,即缠结在一起……

http://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Spaghetti.jpg/175px-Spaghetti.jpg


-1重构和重写是两个非常不同的选项。完全重写,丢弃软件绝不是一个好主意。joelonsoftware.com/articles/fog0000000069.html
P.Brian.Mackey 2011年

1
意大利面代码是IMO的一个更笼统的术语。尽管此类代码通常包含重复字符,但是您可以拥有不带任何重复的意大利面条代码,也可以具有高度重复但没有意大利面条的代码。
彼得Török

@ P.Brian.Mackey我从未说过Refactor和Rewrite是同一回事。OP应该决定使用哪个。
Tom Squires

2
我从不喜欢那篇文章。我同意在引用的示例中重写是一个坏主意,但是仅仅因为在这种情况下重写是一个坏主意,并不意味着它总是一个坏主意。对于初学者来说,进行重写会否阻止其他进度?如果是这样,那就不好了;如果不是的话,那还不算差。
2011年

@jhocking-我认为不重写策略的重点不是阻止其他程序的进度,而是随着时间的流逝丢失真正的修复程序。那个神奇的“ if(goldenNugget> 22)”这个名字虽然不好用,但它仍然解决了一个特定的问题,如果不花费数小时或数天的研究,就很难确定。现在,取而代之的是获取相同的数据并对其进行改进。那是重构。把它扔掉并说“我们下次再做”是浪费时间。解决已经解决的问题的相同原因是浪费时间。在坏账之上添加好的代码会增加债务
P.Brian.Mackey 2011年

0

您说不能完全责怪A-但是以这种方式复制课程实际上是不可原谅的。这是您的代码审查过程的巨大失败。这可能是最大的失败,根本没有代码审查。

如果您曾经想过必须在截止日期前生成糟糕的代码,那么此后发布的绝对最高优先级请求应该是立即修复糟糕的代码。这样您的问题就解决了。

DRY原则适用于不太理想且可以改进的情况。复制课程是一种全新的技能。我首先将其称为“重复代码”,随着时间的流逝,它将变成有史以来最浪费的时间,“差异重复代码”:同一代码的多个副本几乎相同,但并不完全相同,而且没人知道差异是故意的,巧合的或错误的。


-3

这次聚会晚了将近十年,但这种反模式的名称是Divergent Change,其中多个类包含相同行为的副本,但是随着时间和维护进度的发展而被遗忘,有些行为却有所不同。

根据共享行为的含义及其共享方式,可以将其称为Shotgun Surgery,不同之处在于,此处的单个逻辑功能由许多类提供,而不是由单个类提供。


1
这看起来不一样。发散变更是指对同一班级进行许多变更。OP描述了代码重复。gun弹枪手术也不是一回事。gun弹枪外科手术描述了对几个不同类别进行的相同更改。
罗伯特·哈维
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.