通过DRY和OOD引入代码耦合


14

我正在寻找有关DRY与代码耦合的指南。我不喜欢复制代码,也不喜欢无关模块之间的代码耦合。因此,如果在引入重复项一年后发现完全相同的重复代码,我将重构重复代码。但是,我越来越多地体验到现实世界中更加难以预测的情况,并且在重构代码之后,出现了一些情况,需要再次分叉代码。

例如,如果我有处理汽油车,汽油SUV,电动汽车和电动SUV的代码,可以说我将重复的代码重构为“汽油”层次结构和“电动”层次结构,它们均从“车辆”层次结构中衍生而来。到目前为止,一切都很好。然后,我的公司推出了混合动力汽车和混合动力汽车Semi-需要对我的原始等级本身进行核心更改。可能需要在汽油和电气层次之间进行“组合”。

显然,代码重复是不好的,因为它增加了实现上述所有产品共有的更改所花费的时间。但是重构通用代码使引入产品特定的变体同样困难,并且当人们不得不找到代码行来修复错误时,会导致很多“类跳转”-上级父类中的一项更改可以触发所有后代之间的回归错误。

如何在DRY与有害代码耦合之间达到最佳平衡?


1
最好不要盲目遵循规则,而要首先动脑。
gnasher729

足够公平-因此是准则,而不是规则。
user2549686'6

您是否已阅读DRY(或以前称为OAOO)上的Wiki页面?wiki.c2.com/?OnceAndOnlyOnce这是发生许多最初讨论的地方。(实际上,Ward专门为讨论模式和实践而发明了Wiki。)
JörgW Mittag

即使问题听起来不像是重复,也有非常相关的答案:softwareengineering.stackexchange.com/questions/300043/…–
绿巨人

Answers:


8

可以说,我将重复的代码重构为“汽油”层次和“电气”层次,它们均源于“车辆”层次。到目前为止,一切都很好。

然后,我的公司推出了混合动力汽车和混合动力汽车Semi-要求对我的原始等级本身进行核心更改。可能需要在汽油和电气层次之间进行“组合”

我认为这是人们转向继承而非继承的主要原因之一。

当您进行了所描述的概念更改时,继承会迫使您进行大规模的重组。

当重组“太大/很难”时,人们会编写重复的代码来避免它。

无需通过将代码移到继承链上来远程复制副本,而是可以将其移出到帮助器类或服务中,然后在需要时将该类作为组合的一部分注入。

最终的设计是否为OOP尚有争议


1
+1指出继承是问题,而不是DRY。重用代码的最简单方法是删除不必要的依赖关系,并且人们经常错过这样的事实:使用继承时,父类(及其后代)都是依赖关系。仅当减少或消除依赖性时,您才真正获得可重用的代码。
格雷格·伯格哈特

3
最终的设计是否为OOP尚有争议 ”。一切都可以辩论。要问的问题是,是否可以进行理性的理性辩论?答案是不。我认为您的最后一段不利于本来很好的答案。
David Arno

@davidarno真的不跟随您,我读过OOD标题为“面向对象设计”吗?因此,我在不解决辩论的情况下就解决了这个问题
Ewan

1
@Ewan:我和David Arno在这里。我认为,二十多年来,众所周知“如果没有继承,那就不是OOP”是谬论。
布朗

jeeze正是我试图避免的那种讨论。双方都有意见,没有明确的答案
Ewan

3

您是正确的,遵循DRY原理,可以增加原本不相关的模块之间的耦合。特别是在较大的软件系统中,这可能导致不遵循DRY可能是更好的选择。

不幸的是,您的示例不太适合证明这一点-此处描述的问题是由错误的次佳用法错误导致的经典错误引起的。但是,对于我上面所写的内容,将通用代码重构为通用基类还是助手类(组成)都没有关系。在这两种情况下,都可能不得不将公共代码放入库L中,并从两个以前不相关的程序A和B中引用该库。

让我们假设A和B之前是完全不相关的,可以分别对其进行版本控制,发布和部署。但是,通过将通用代码放入共享库L中,对A的新要求可能会导致对L的更改,从而可能导致对B的更改。因此,这导致需要进行额外的测试,并且可能导致B的新发行和部署周期。

那么,如果您不愿意放弃DRY原则,该如何处理呢?好吧,有一些众所周知的策略可以解决这个问题:

  1. 将A,B和L保留为同一产品的一部分,具有一个通用的版本号,一个通用的构建,发布和部署过程,并具有高度的自动化

  2. 或让L自己成为具有次要版本号(没有不兼容的更改)和主版本号(可能包含重大更改)的产品,然后让A和B允许每个人引用L的不同版本行。

  3. 使L尽可能为SOLID,并注意向后兼容性。L中无需修改(OCP)即可重用的模块越多,重大更改发生的可能性就越小。“ SOLID”中的其他原则也有助于实现该目标。

  4. 对L以及A和B都使用自动测试,尤其是。

  5. 小心输入到L中的内容。应该仅存在于系统中某个位置的业务逻辑是一个不错的选择。看起来“相似”并且将来可能会有所不同的事物是不好的选择。

请注意,当A和B由不同的,无关的团队开发,维护和发展时,DRY原理变得不那么重要-DRY涉及可维护性和可发展性,但是让两个不同的团队提供各自的维护工作有时可能比捆绑他们的产品更有效。在一起,因为有一点重复使用。

所以最后,这是一个权衡。如果要在大型系统中遵循DRY原则,则需要投入更多的精力来创建强大的可重用组件-通常比您期望的更多。您必须相信自己的判断何时值得,何时不值得。


1

干是另一个结构规则。这意味着,如果将其极端化,就如同忽略它一样糟糕。这是一个比喻,可以让您摆脱结构性思维定势。

爱你的双胞胎。杀死克隆。

当您盲目复制和粘贴以避免键盘输入时,您会无意识地创建克隆。当然,他们会按照您的意愿进行操作,但他们却使您不知所措,因为现在更改行为非常昂贵。

当您因职责不同而使用相同的代码重现相同的行为时,现在碰巧具有相同的需求,但是这可能会使您面临不同的更改,因此您有一个双胞胎,随着时间的流逝,该双胞胎应可以自由更改并成长。

您可以随意扫描他们的DNA(代码)。克隆人和双胞胎很难分辨,如果您要做的就是看它们。您需要了解他们生活的环境。他们为什么出生。他们的最终命运可能是什么。

假设您在ABC公司工作。它由三名公司高管A,B和C负责。他们都希望您制作软件产品,但是他们每个人都有自己的部门。他们都希望您从小处着手。现在只输出一条简单的消息。所以你写“ Hello World”。

讨厌它,希望您在此处输入公司名称。

B喜欢它,希望您不要理会它,并将公司名称添加到初始屏幕。

C只是想要一个计算器,并认为该消息只是您入门的一种方式。

您确定的一件事是,这些人对本项目的想法有很大不同。有时他们会同意。有时候,他们会将您拉向不同的方向。

当您由于要创建使代码独立变化的能力而复制代码时,就是在创建双胞胎。当您因为键入而感到痛苦并且复制和粘贴很容易而复制代码时,您正在创建邪恶的克隆。

A,B和C是您的代码必须服务的不同母版。没有哪一行代码可以长期服务多个主机。


首先,非常感谢所有评论。
user2549686 '18
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.