您能容忍多少重复代码?
在最近的一次代码审查中,我发现类中存在几行重复的逻辑(少于 15 行)。当我建议作者重构代码时,他认为这样代码更容易理解。再次阅读代码后,我不得不同意提取重复的逻辑会稍微损害可读性。
我知道 DRY 是指导方针,而不是绝对规则。但总的来说,你愿意以 DRY 的名义损害可读性吗?
In a recent code review I spotted a few lines of duplicated logic in a class (less than 15 lines). When I suggested that the author refactor the code, he argued that the code is simpler to understand that way. After reading the code again, I have to agree extracting the duplicated logic would hurt readability a little.
I know DRY is guideline, not an absolute rule. But in general, are you willing to hurt readability in the name of DRY?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
我一点也不容忍。由于时间限制或其他原因,我可能最终会得到一些。但我仍然没有发现重复代码确实有必要的情况。
说它会损害可读性只表明你不擅长选择名称:-)
I tolerate none. I may end up having some due to time constraints or whatnot. But I still haven't found a case where duplicated code is really warranted.
Saying that it'll hurt readability only suggests that you are bad at picking names :-)
如果相关代码有明确的业务或技术支持目的 P,那么您通常应该重构它。否则,您将遇到克隆代码的经典问题:最终您会发现需要修改支持 P 的代码,并且您将找不到实现它的所有克隆。
有些人认为 3 个或更多副本是重构的门槛。我相信,如果你有两个,你应该这样做;在一个大系统中找到其他克隆[或者甚至知道它们可能存在]是很困难的,无论你有两个、三个还是更多。
现在,这个答案是在没有任何工具来查找克隆的情况下提供的。如果您能够可靠地找到克隆,那么重构的最初原因(避免维护错误)就不那么有说服力了(拥有命名抽象的效用仍然是真实的)。您真正想要的是一种查找和跟踪克隆的方法;抽象它们是确保您可以“找到”它们的一种方法(通过使查找变得微不足道)。
一个能够可靠地找到克隆的工具至少可以防止您出现更新克隆失败的维护错误。其中一个工具(我是作者)是 CloneDR。 CloneDR 使用目标语言结构作为指导来查找克隆,从而无论空格布局、注释更改、重命名变量等如何查找克隆。(它已针对多种语言实现,包括 C、C++、Java、C#、COBOL 和 PHP) )。 CloneDR 将在大型系统中查找克隆,而无需提供任何指导。显示检测到的克隆,以及反统一符,这本质上是您可能编写的抽象。它的版本(针对 COBOL)现在与 Eclipse 集成,并显示您何时在缓冲区中的克隆内进行编辑,以及其他克隆的位置,以便您可以在其中检查/修改其他克隆。 (你可以做的一件事就是重构它们:)。
我曾经认为克隆是完全错误的,但人们这样做是因为他们不知道克隆将如何与原始克隆不同,因此在克隆行为发生时最终的抽象尚不清楚。现在我相信克隆是好的,如果你可以跟踪克隆并在抽象变得清晰后尝试重构。
If the code in question has a clear business or technology-support purpose P, you should generally refactor it. Otherwise you'll have the classic problem with cloned code: eventually you'll discover a need to modify code supporting P, and you won't find all the clones that implement it.
Some folks suggest 3 or more copies is the threshold for refactoring. I believe that if you have two, you should do so; finding the other clone(s) [or even knowing they might exist] in a big system is hard, whether you have two or three or more.
Now this answer is provided in the context of not having any tools for finding the clones. If you can reliably find clones, then the original reason to refactor (avoiding maintenance errors) is less persausive (the utility of having a named abstraction is still real). What you really want is a way to find and track clones; abstracting them is one way to ensure you can "find" them (by making finding trivial).
A tool that can find clones reliably can at least prevent you from making failure-to-update-clone maintenance errors. One such tool (I'm the author) is the CloneDR. CloneDR finds clones using the targeted langauge structure as guidance, and thus finds clones regardless of whitespace layout, changes in comments, renamed variables, etc. (It is implemented for a number a languages including C, C++, Java, C#, COBOL and PHP). CloneDR will find clones across large systems, without being given any guidance. Detected clones are shown, as well as the antiunifier, which is essentially the abstraction you might have written instead. Versions of it (for COBOL) now integrate with Eclipse, and show you when you are editing inside a clone in a buffer, as well as where the other clones are, so that you may inspect/revise the others while you are there. (One thing you might do is refactor them :).
I used to think cloning was just outright wrong, but people do it because they don't know how the clone will vary from the original and so the final abstraction isn't clear at the moment the cloning act is occurring. Now I believe that cloning is good, if you can track the clones and you attempt to refactor after the abstraction becomes clear.
一旦您重复任何内容,您就会创建多个位置来进行编辑,如果您发现自己犯了错误,需要扩展它、编辑、删除或其他数十种情况您可能会遇到迫使您做出改变的原因。
在大多数语言中,将块提取到适当命名的方法很少会损害您的可读性。
这是你的代码,有你的标准,但我对你的“多少?”的基本回答。 没有 ...
As soon as you repeat anything you're creating multiple places to have make edits if you find that you've made a mistake, need to extend it, edit, delete or any other of the dozens of other reasons you might come up against that force a change.
In most languages, extracting a block to a suitably named method can rarely hurt your readability.
It is your code, with your standards, but my basic answer to your "how much?" is none ...
你没有说是什么语言,但在大多数 IDE 中,它是一个简单的重构 ->提取方法。这要容易得多,而且带有一些参数的单个方法比 2 个重复代码块更易于维护。
you didn't say what language but in most IDEs it is a simple Refactor -> Extract Method. How much easier is that, and a single method with some arguments is much more maintainable than 2 blocks of duplicate code.
很难抽象地说。但我自己的信念是,即使是一行重复的代码也应该被制作成一个函数。当然,我自己并不总能达到这个高标准。
Very difficult to say in abstract. But my own belief is that even one line of duplicated code should be made into a function. Of course, I don't always achieve this high standard myself.
重构可能很困难,这取决于语言。所有语言都有局限性,有时重复逻辑的重构版本在语言上可能比重复代码更复杂。
当两个具有不同基类的对象在操作方式上有相似之处时,经常会出现逻辑代码重复。例如,两个 GUI 组件都显示值,但没有实现用于访问该值的通用接口。重构这种系统要么需要方法采用比所需更多的通用对象,然后进行类型检查和转换,要么需要重新考虑类层次结构。重组。
这种情况与代码完全重复的情况不同。如果我只想使用它两次,并且两次都在同一个函数中,我不一定会创建一个新的接口类。
Refactoring can be difficult, and this depends on the language. All languages have limitations, and sometimes a refactored version of duplicated logic can be linguistically more complex than the repeated code.
Often duplications of code LOGIC occur when two objects, with different base classes, have similarities in the way they operate. For example 2 GUI components that both display values, but don't implement a common interface for accessing that value. Refactoring this kind of system either requires methods taking more generic objects than needed, followed by typechecking and casting, or else the class hierarchy needs to be rethought & restructured.
This situation is different than if the code was exactly duplicated. I would not necessarily create a new interface class if I only intended it to be used twice, and both times within the same function.
DRY的要点是可维护性。如果代码更难理解,那么维护起来就更困难,因此,如果重构损害了可读性,您实际上可能无法达到 DRY 的目标。对于少于 15 行的代码,我倾向于同意你同学的观点。
The point of DRY is maintainability. If code is harder to understand it's harder to maintain, so if refactoring hurts readability you may actually be failing to meet DRY's goal. For less than 15 lines of code, I'd be inclined to agree with your classmate.
可读性是代码可以拥有的最重要的东西之一,我不愿意在它上妥协。重复的代码是一种难闻的气味,而不是一种死罪。
话虽如此,这里存在问题。
如果这段代码应该是相同的,而不是巧合地相同,那么就会存在可维护性风险。我会在每个地方都有评论指向另一个地方,如果需要放在第三个地方,我会重构它。 (我实际上确实有这样的代码,在两个不共享适当代码文件的不同程序中,因此每个程序中的注释都指向另一个程序。)
您还没有说这些行是否构成一个连贯的整体,执行您的某些功能可以很容易地描述。如果确实如此,请将其重构。情况不太可能是这样,因为您同意代码嵌入两个位置更具可读性。但是,您可以寻找更大或更小的相似性,并且也许可以提取一个函数来简化代码。仅仅因为重复十几行代码并不意味着函数应该仅由那十几行组成。
Readability is one of the most important things code can have, and I'm unwilling to compromise on it. Duplicated code is a bad smell, not a mortal sin.
That being said, there are issues here.
If this code is supposed to be the same, rather than is coincidentally the same, there's a maintainability risk. I'd have comments in each place pointing to the other, and if it needed to be in a third place I'd refactor it out. (I actually do have code like this, in two different programs that don't share appropriate code files, so comments in each program point to the other.)
You haven't said if the lines make a coherent whole, performing some function you can easily describe. If they do, refactor them out. This is unlikely to be the case, since you agree that the code is more readable embedded in two places. However, you could look for a larger or smaller similarity, and perhaps factor out a function to simplify the code. Just because a dozen lines of code are repeated doesn't mean a function should consist of that dozen lines and no more.
一般来说,没有。无论如何,不是为了可读性。总有某种方法可以将重复的代码重构为揭示通用方法的意图,读起来就像一本书,IMO。
如果您想提出违反 DRY 的论点,以避免引入依赖关系,这可能会更有分量,您可以获取 Ayende 的固执己见的意见以及代码来说明这一点 此处。
除非你的开发人员实际上是 Ayende,否则我会坚持 DRY 并通过意图揭示方法获得可读性。
乙肝
In general, no. Not for readability anyway. There is always some way to refactor the duplicated code into an intention revealing common method that reads like a book, IMO.
If you want to make an argument for violating DRY in order to avoid introducing dependencies, that might carry more weight, and you can get Ayende's opinionated opinion along with code to illustrate the point here.
Unless your dev is actually Ayende though I would hold tight to DRY and get the readability through intention revealing methods.
BH
这实际上取决于很多因素,代码的使用量、可读性等。在这种情况下,如果只有一份代码并且这种方式更容易阅读,那么也许就可以了。但如果您需要在第三个地方使用相同的代码,我会认真考虑将其重构为通用函数。
It really depends on many factors, how much the code is used, readability, etc. In this case, if there is just one copy of the code and it is easier to read this way then maybe it is fine. But if you need to use the same code in a third place I would seriously consider refactoring it into a common function.
我不接受重复的代码。如果某个东西在多个地方使用,它将成为框架的一部分或至少是一个实用程序库。
最好的一行代码是一行没有写出来的代码。
I accept NO duplicate code. If something is used in more than one place, it will be part of the framework or at least a utility library.
The best line of code is a line of code not written.
重构:改进现有代码的设计
< strong>三法则
三击即可重构。
工作中的程序员< /em>
Refactoring: Improving the Design of Existing Code
The Rule of Three
Three strikes and you refactor.
Coders at Work
就我个人而言,我更喜欢首先保持代码易于理解。
DRY 是为了简化代码的维护。在许多情况下,为了删除重复的代码而使代码变得更难以理解比使用一些重复的代码更会损害可维护性。
话虽如此,我确实同意 DRY 是一个在实际可行的情况下值得遵循的良好目标。
Personally, I prefer keeping code understandable, first and foremost.
DRY is about easing the maintenance in code. Making your code less understandable in order to remove repeated code hurts the maintainability more, in many cases, than having some repeated lines of code.
That being said, I do agree that DRY is a good goal to follow, when practical.