【发布时间】:2010-11-03 00:43:53
【问题描述】:
与复制/粘贴相比,重用代码的最佳做法是什么?
重用的问题可能是更改重用的代码会影响许多其他功能。
这有好有坏:如果更改是错误修复或有用的增强,则很好。如果其他重用代码由于依赖于旧版本(或新版本有错误)而意外损坏,则很糟糕。
在某些情况下,复制/粘贴似乎更好 - 粘贴代码的每个用户都有一个私人副本,可以自定义该副本而不会产生任何后果。
是否有解决此问题的最佳实践;重用是否需要防水单元测试?
【问题讨论】:
与复制/粘贴相比,重用代码的最佳做法是什么?
重用的问题可能是更改重用的代码会影响许多其他功能。
这有好有坏:如果更改是错误修复或有用的增强,则很好。如果其他重用代码由于依赖于旧版本(或新版本有错误)而意外损坏,则很糟糕。
在某些情况下,复制/粘贴似乎更好 - 粘贴代码的每个用户都有一个私人副本,可以自定义该副本而不会产生任何后果。
是否有解决此问题的最佳实践;重用是否需要防水单元测试?
【问题讨论】:
一般来说,复制和粘贴是个坏主意。但是,与任何规则一样,这也有例外。由于例外情况不如规则广为人知,因此我将重点介绍一些重要的例外情况:
你有一个非常简单的设计,你不想用设计模式和 OO 的东西把它弄得更复杂。您有两个或三个案例,它们以无数微妙的方式变化,即这里有一条线,那里有一条线。从问题的性质您知道,您可能不会有超过 2 或 3 个案例。有时,与设计地狱般的东西来解决这样一个相对简单的问题相比,仅仅剪切和粘贴可能是两害相权取其轻。代码量有其成本,但概念复杂性也是如此。
您有一些非常相似的代码现在,但该项目正在迅速发展,您预计这两个实例会随着时间的推移出现显着差异,甚至试图识别相当大的,可分解的功能块将保持通用,更不用说将它们重构为可重用的组件,这将比它的价值更麻烦。这适用于您认为对一个实例进行不同更改的可能性远大于对通用功能进行更改的可能性。
【讨论】:
复制和粘贴的一个非常合适的用法是Triangulation。为一个案例编写代码,查看有一些变化的第二个应用程序,复制并粘贴到新的上下文中 - 但你还没有完成。如果你在那个时候停止,你就会遇到麻烦。复制此代码(可能有细微的变化)会暴露您的代码需要的一些常见功能。一旦它在两个地方都经过测试,并且在两个地方都可以工作,您应该将这种共性提取到一个地方,从两个原始地方调用它,然后(当然)重新测试。
如果您担心从多个地方调用的代码会带来脆弱性风险,那么您的函数可能不够细粒度。过于粗粒度的函数,功能太多,难以重用,难以命名,难以调试。找到功能的原子位,命名并重用它们。
【讨论】:
复制/粘贴会导致不同的功能。代码开始时可能相同,但随着时间的推移,一个副本中的更改不会反映在所有其他副本中。
此外,在非常简单的情况下,复制/粘贴可能看起来“没问题”,但它也开始让程序员进入一种可以复制/粘贴的心态。这就是“滑坡”。当重构应该是正确的方法时,程序员开始使用复制/粘贴。您必须始终小心设置先例以及向未来开发人员发送的信号。
甚至有一个比我更有经验的人引用了这个,
“如果您在编码时使用复制和粘贴,您可能会犯设计错误。”
-- David Parnas
【讨论】:
所以消费者(重用者)代码依赖于重用代码,没错。
您必须管理这种依赖关系。
二进制重用(例如 dll)和代码重用(例如脚本库)也是如此。
消费者应依赖于重用代码/二进制文件的某个(已知)版本。
消费者应保留一份重复使用的代码/二进制文件,但切勿直接修改它,只有在安全的情况下更新到较新的版本。
在修改重用代码库时要仔细考虑。 重大更改的分支。
如果消费者想要更新重用的代码/二进制文件,那么它首先必须测试它是否安全。如果测试失败,那么消费者总是可以回退到最后一个已知(并保留)的好版本。
因此,您可以从重用中受益(例如,您必须在一个地方修复错误),并且您仍然可以控制更改。但是,每当您更新重用的代码/二进制文件时,没有什么可以让您免于测试。
【讨论】:
有一些指标可以用来衡量您的代码,并且由您(或您的开发团队)决定一个适当的阈值。 Ruby on Rails 具有“Metric-Fu” Gem,它包含许多工具,可以帮助您重构代码并使其保持最佳状态。
我不确定有哪些工具可用于其他语言,但我相信有一个适用于 .NET。
【讨论】:
你写的地方:
重用的问题可能是 更改重用的代码会影响 许多其他功能。 ...在某些情况下,似乎 复制/粘贴更好 - 每个用户 粘贴的代码有一个私人副本 它可以在没有的情况下进行自定义 后果。
我认为您已经扭转了与复制粘贴相关的顾虑。如果你将代码复制到 10 个地方,然后需要对行为进行轻微修改,你会记得在所有 10 个地方都进行更改吗?
不幸的是,我处理过大量庞大、草率的代码库,通常您会看到的结果是相同的 4 行代码的 20 个版本。其中一些(通常很小)子集有 1 个微小的变化,其他一些小的(并且只是部分相交的子集)有一些其他的微小变化,不是因为变化是正确的,而是因为代码被复制和粘贴了 20 次并且几乎应用了更改,但并不完全一致。
到了这一点,几乎不可能分辨出这些变体中的哪些是有原因的,哪些是由于错误而存在的(而且由于更常见的是疏忽错误 - 忘记应用补丁而不是更改东西 - 不太可能有任何证据或cmets)。
如果您需要不同的功能,请调用不同的功能。如果您需要相同的功能,请避免复制粘贴,以免跟随您的人保持理智。
【讨论】:
复制和粘贴从来都不是好习惯。有时,在非常糟糕的代码库中作为短期修复似乎更好,但在设计良好的代码库中,您将拥有以下内容,便于重用:
如果您的代码库具有这些属性,那么复制和粘贴将永远不会是更好的选择。正如 S Lott 所说,不必要地增加代码库的大小会产生巨大的成本。
【讨论】:
对此有最佳实践吗 问题;重复使用需要防水吗 单元测试?
是的,是的。重写一次你已经做对的代码从来都不是一个好主意。如果您从不重用代码而只是重写它,那么您的错误表面就会增加一倍。与许多最佳实践类型的问题一样,Code Complete 改变了我的工作方式。是的,尽你最大的能力进行单元测试,是的,重用代码并获得一份Code Complete 的副本,然后一切就绪。
【讨论】:
使用复制和粘贴几乎总是一个坏主意。正如您所说,您可以进行测试以检查以防您破坏某些东西。
关键是,当你调用一个方法时,你不应该真正关心它是如何工作的,而应该关心它做了什么。如果你改变方法,改变它的作用,那么它应该是一个新方法,或者你应该检查这个方法在哪里被调用。
另一方面,如果更改没有修改方法的作用(仅修改方式),那么您在其他地方不应该有问题。如果你这样做了,你做错了什么......
【讨论】:
在我看来,在多个地方使用的一段代码可能会针对一个地方而不是另一个地方进行更改,这似乎没有遵循适当的范围规则。如果两个不同的事情需要“相同”的方法/类来执行两个不同的功能,那么应该拆分该方法/类。
不要复制/粘贴。如果确实需要修改一个地方的代码,那么您可以扩展它,可能通过继承、重载,或者如果必须,复制和粘贴。但不要一开始就复制粘贴相似的片段。
【讨论】:
您应该编写单元测试,虽然是的,但从某种意义上说,克隆代码可以给您一种安全感,即您的更改不会影响大量其他例程,但这可能是一种错误的安全感。基本上,你的安全感来自于不知道如何使用代码。 (这里的无知不是贬义词,只是因为无法了解代码库的所有内容)习惯使用 IDE 了解代码在哪里使用,习惯阅读代码以了解如何使用正在使用中。
【讨论】:
每一行代码都有成本。
Studies 表明成本与代码行数不是线性的,而是指数级的。
复制/粘贴编程是重用软件最昂贵的方式。
“重用是否需要防水单元测试?”
没有。
所有代码都需要足够的单元测试。所有代码都可以重用。
【讨论】: