在特殊情况下,我们可以接受重复的代码吗?


57

我正在一个软件项目中,我们必须构建三个API。一个用于家庭银行业务渠道,一个用于代理渠道,第三个用于移动渠道。

代理API是最完整的API,因为它具有所有功能。然后是更小的Home API,然后是移动API。

此处的架构师建立了一个公共层(所有API共享的跨通道EJB服务)。但是那时的API是不同的。

目前,这些API之间没有太大的区别。大型团队从代理商渠道开始,现在我们正在对其进行调整以适合家庭渠道。我们只是在为我们的家庭应用专门添加对象。否则,API之间的代码相似度为95%。这些API建立在Spring MVC之上,并且具有(控制器,模型和一些实用程序)。

基本上,控制器正在将BO映射到ChannelObject(在我看来,这样做不合适的地方),以及一些额外的实用程序和序列化程序。目前所有重复项。他们说重复的原因是他们希望API独立。“如果明天我们想要的家庭行为不同于代理商或移动设备,我们将不会挣扎!”

是否存在我们应该接受重复代码的情况?


22
而且,如果在未来的明天三天,他们决定要在所有API之间进行一致的数据访问和表示……嗯……“奋斗!”
Becuzz

26
重复的代码不一定是一件坏事。俗话说“干是脱钩的敌人”。话虽如此,为未来而不是现在设计,确实是一件很不好的事情。那个未来几乎永远不会过去。取而代之的是,设计一个高度解耦的解决方案,并针对当前需要进行良好的自动化测试。然后,在将来,如果需要一些其他的东西,将更容易进行更改。
David Arno

2
我认为我从不后悔重复代码的两种情况是(a)重复代码只占很小的一部分,但不是很重要;(b)从已经死掉的系统中复制代码成为旨在延长寿命的新系统。在很多其他情况下,我在分叉代码后哭得很痛。
迈克尔·凯

14
在我工作过的地方,经常有人指出,应该(通常)重复一次,并第三次抽象出通用性。这就消除了时代精神,避免了早期的,可能不合适的抽象,这种抽象增加了耦合而不是凝聚力。当真正理解了将来的要求时,当然可以例外。
Pieter Geerkens'8

3
如果重复代码可以自动生成,那么其中一种可以接受的重复代码的琐碎情况是可以自动生成的
samgak

Answers:


70

复制可能是正确的选择,但并非出于这个原因。

说“即使现在它们是相同的,我们可能希望代码库中的这三个位置表现不同”,这并不是大规模复制的好理由。这个概念可能适用于每个系统,并且可以用来证明任何重复是合理的,这显然是不合理的。

仅当删除时才应允许重复,否则由于其他一些原因,复制现在总体上会变得更加昂贵(暂时无法想到一个好的复制品,但可以肯定的是,复制是可行的-编程中的几乎所有内容都是折衷而不是法)。

对于您正在做的事情,正确的解决方案可能是例如将立即复制的行为提取到策略或某种将行为建模为类的其他模式中,然后使用同一类的三个实例。这样,当您确实要在三个位置之一中更改行为时,只需创建一个新的Strategy并将其实例化在一个位置。这样,您只需要添加一些类,而剩下的代码库几乎完全不受影响。


1
如果两件事碰巧表现相同,那就是重复。尽管除非出于某种原因它们的行为相同,否则不应合并它们。能否提取实现的一些常见部分?谁知道,有时有些构建块尚未抽象。
Deduplicator

32
举例来说,我们的应用程序的三个部分使用了一大堆代码,并且编写的代码非常糟糕,到处都有许多小的条件分支,以覆盖这三个部分的异常。但是,这是很重要的一点,它已经被大量使用了两年,没有任何重大的错误报告。因此,当应用程序的第四部分需要使用它来做某事,但又稍有不同时,便决定不触摸完美运行的有缺陷的代码,而只是复制它并为它创建一个更好编写的,灵活的基础。未来。
KRyan

2
与可读性成本相比,维护成本最终被捆绑在一起时,重复性是正确的选择。我认为这在这种情况下根本不适用,并且经常在较小的项目中看到,而不是这样。即使这样,也很少会遇到这种情况,如果您有一些利基嵌套的重复项会迫使您在较小的地方使用Template Method模式等,则会发生这种情况。这通常会导致混淆代码。
opa

其中,复制是非常有用的一个例子(与“删除它yould在整体上更加昂贵,现在 ”)在Web应用程序中输入valdation:我们采用第一(可能是简化的)验证的客户端,以便用户获取有关问题及时反馈; 然后我们在服务器上进行相同(或更彻底)的验证,因为客户端不可信。
哈根·冯·埃森

3
@HagenvonEitzen但这甚至不必重复,具体取决于技术堆栈。例如,您可能在浏览器中输入了JavaScript检查,然后在服务器上进行了Java检查,因此您具有重复的功能。但是,如果要在服务器上运行Node.js,则可以在浏览器和服务器上使用相同的JavaScript验证,从而避免重复。您仍然希望代码可以在多个位置(受信任和不受信任的环境)中运行,但是不一定必须重复代码
约书亚·泰勒

87

桑迪·梅茨(Sandi Metz)是Ruby生态系统中的著名软件工程师和作家,她在一篇出色的博客文章中发表演讲,并在演讲中还谈到了复制和抽象之间的关系。她得出以下结论

复制比错误的抽象要便宜得多

我完全同意她的看法。让我为您提供更多有关此报价的背景信息。有时很难找到正确的抽象。在这种情况下,很容易尝试进行任何抽象以减少重复。但是稍后您可能会发现您的抽象并不适用于所有情况。但是,再次更改所有内容并走另一条路线的成本很高(有关更好的解释,请观看她的演讲!)。

所以是的,对我来说,在某些特殊情况下,接受重复是正确的决定,尤其是当您不确定将来会发生什么并且需求可能会发生变化时。从您的帖子中,我认为现在有很多重复项,但是您的同事建议这可能会改变,并且不要将两个应用程序相互耦合。在我看来,这是一个有效的论点,通常不能忽略。


23
是的,如果您无法推断出通用规则,则尝试将其抽象化只会失败。而且,如果通信是巧合,那可能是短暂的。
Deduplicator

5
一旦使用适当的位置,更改抽象可能会很昂贵,但是一旦使用适当的位置,要消除重复可能会带来更多麻烦。当然,这在很大程度上取决于语言等。现代的强静态类型系统可以很好地帮助您获得某些抽象权限的大规模更改。但是,使重复的​​功能保持同步并不是类型系统可以帮助您的事情(因为对于类型系统而言,重复项是完全不同的)。但是我想在动态的,鸭式语言中却相反。因此对于Ruby来说可能有意义。
大约

34

如果人们开始使用“如果明天”一词来进行设计推理,这对我来说通常是一个很大的警告信号,尤其是当该论点用于证明一项包括额外工作和努力的决定的合理性时,没人真正知道这是否会实现还清,这比相反的决定更难更改或还原。

代码重复仅在短期内减少了工作量,但是它将几乎立即增加维护工作量,与代码重复行的数量成正比。还要注意,一旦代码被复制,当发现这是一个错误的决定时,将很难删除复制,而如果现在不复制代码,则如果后来发现坚持使用DRY,仍然很容易引入复制。是错误的决定。

据说,在较大的组织中,有时有利于支持不同团队独立于DRY原则。如果通过提取两个API的95%共同部分来消除重复,则新组件导致两个原本独立的团队的结合,这可能不是最明智的决定。另一方面,如果您的资源有限,并且只有一个团队维护这两个API,那么我相信不做任何双重努力并避免任何不必要的代码重复对他们自己有利。

还要注意,如果“ Home”和“ Agency” API仅由完全不同的应用程序使用,或者如果人们可能试图在也可以在“ Home”上下文中使用的那些API之上编写组件构建,则这将有所不同。就像在“代理商”上下文中一样。在这种情况下,使API的公共部分完全相同(只有在公共部分不重复的情况下才能保证),这可能会使此类组件的开发更加容易。

因此,如果事实证明确实存在不同的子团队,每个子团队负责每个API,每个子团队具有不同的时间表和资源,那么该是时候复制代码了,但不是“以防万一”。


3
对我来说,比“如果明天”更大的警告信号是“那将永远不会改变”。
abuzittin gillifirca

@abuzittingillifirca:这和问题或我的答案有什么关系?
布朗

1
我在挑战你的第一段。
abuzittin gillifirca

2
@abuzittingillifirca:好吧,再读一遍这个问题:这是关于用“如果明天”的论据来推理重复的决定的理由,这种决定很难还原,因此实际上使得在某些情况下该软件更难更改。这可能有点违反直觉,但是使软件对未来保持可更改性的最佳方法不是对未来做出任何(可能是错误的)假设,而是使软件尽可能的小和SOLID。
布朗

13

复制以防止耦合。假设您有两个大型系统,并强迫它们使用相同的库。您可能正在耦合两个系统的发布周期。这可能还不错,但是假设一个系统需要进行更改。其他需要分析更改,并且可能会受到影响。有时它可能会破坏事情。即使双方都能协调变更,也可能要召开许多会议,包括经理,测试,依存关系以及小型自治团队的结束。

因此,您要付出重复代码的代价,以获得自治和独立性。


因此,这是组件间重复以避免耦合。但是您仍然希望避免组件内重复,对吗?
TemplateRex

好吧,是的,您想减少重复的代码,因为这会使您的代码更易于理解和修改。但是请记住,除了可行的例外,还有很好的答案。避免重复的正确抽象可能很难找到。
博尔嘉(Borjab),
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.