是有代码重复并且非常简单/可读更好,还是没有重复(使用泛型)但要复杂得多?
一般来说,我经常遇到这种情况。我的一些同事更喜欢非常简单、易于阅读的类,即使这意味着存在一些代码重复,而我会尽我所能避免代码重复,即使这意味着使架构变得更加复杂。最佳实践是什么?我只用 Java 工作。
In general I come across this a lot. Some of my co-workers prefer very simple, easy to read classes even if that means that there is some code duplication, whereas I do everything in my power to avoid code duplication, even if it means making more a complicated architecture. What is the best practice? I work exclusively in Java.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
虽然这两个目标都很好,但我认为可读性绝对是拥有可维护代码库的首要要求。我总是更喜欢简单、可读和可维护,而不是完全消除代码重复。
While both are good goals, I feel that readability is the absolute first requirement to have a maintainable codebase. I would always prefer simple, readable, and maintainable to a complete elimination of code duplication.
我总是喜欢没有重复代码的解决方案。即使更复杂的架构一开始更难理解,但维护的好处远远超过了学习曲线。
I always favor the solution with no duplication of code. Even if the more complicated architecture is harder to understand at first, the benefits to maintenance more than outweigh the learning curve.
最佳实践:如果代码很短,请将其复制两次,但切勿重复。
因此,如果您在 3 个不同的位置复制/粘贴了非常相似的代码片段,请考虑重构。
请记住,重构并不自动意味着使代码变得更加复杂。请考虑以下事项:
每次您需要一个新数据类型的堆栈时,您都需要编写一个新类。复制代码工作正常,但假设您想添加一个新方法,也许是一个返回新堆栈的“Push”方法?好吧,现在你不得不将它添加到无数不同的地方。或者您可以使用通用对象堆栈,但这样您就会失去类型安全性。泛型将简化架构:
酷!
好吧,这个例子怎么样:
好吧,所以每次添加一个方法时,你都必须检查你正在使用哪种类型的记录器。痛苦,并且容易出现错误。通常,您会分解出一个接口(可能使用 Log、Clear 和 Truncate 方法),然后创建三个类(FileLogger、DatabaseLogger、ConsoleLogger)。
更多类=更多架构。从长远来看,这是更容易还是更难维护?对于这个例子,我想说代码现在更容易维护,但是 YMMV。
Best practice: If the code is short, duplicate it twice, but never more.
So, if you have very similar snippets of code copy/pasted in 3 different places, consider refactoring.
Keep in mind, refactoring doesn't automaticaly mean making code more complicated. Consider the following:
Everytime you want a stack for a new datatype, you need to write a new class. Duplicating code works fine, but let's say you want to add a new method, maybe a "Push" method which returns a new stack? Alright, now you're forced to add it in a bajillion different places. Or you could use a generic Object stack, but then you'd lose type-safety. Generics will simplify the architecture:
Cool!
Alright, how about this example:
Alright, so each time you add a method, you have to check what kind of logger you're using. Painful, and prone to bugs. Normally, you'd factor out an interface (probably with the methods Log, Clear, and Truncate), then create three classes (FileLogger, DatabaseLogger, ConsoleLogger).
More classes = more architecture. Is this easier or harder to maintain in the longer run? For this example, I'd say the code is now easier to maintain, but YMMV.
避免代码重复的主要原因是可维护性。如果一段代码出现在多个位置,那么当需要更新时,您必须记住每个地方都进行更改。忘记更改一个实例可能会导致大问题,您可能不会立即注意到。
The main reason to avoid code duplication is maintainability. If a segment of code appears in multiple places, when it comes time to update you have to remember to change it everywhere. Forgetting to change one instance can cause big problems, which you may not notice immediately.
在某些极端情况下,您可以通过复杂的元编程(对于 Java 来说不是太大问题)或过度使用反射来防止代码重复,在这少数情况下,我倾向于允许重复。这是很少见的。只要代码仍然可以被熟练的开发人员(而不是您)理解,我就会消除重复。
我遇到过这样的情况:一个团队包括一两个熟练的开发人员和一群新手,新手试图阻止使用他们一眼看不懂的编码方法。必须抵制这种行为。
There are extreme cases where you prevent code duplication by complicated metaprogramming (not so much an issue for Java) or excessive use of reflection, and in those few cases I'd favor permitting the duplication. This is rare. So long as the code remains understandable by a reasonably skilled developer who isn't you, I'd go for eliminating the duplication.
I have run across situations where a team includes one or two skilled developers and a bunch of newbies, where the newbies try to prevent the use of coding approaches that they don't understand at a glance. This must be resisted.
我不明白“泛型”与你的问题有什么关系。使用单独的类来表示 CollectionOfFoo 和 CollectionOfBar 是微不足道的,显然是错误的,因此这不是您所要求的。
你可能必须为每个观点提供一个例子,但你仍然可能会因为主观而被关闭。
I don't see how "generics" is related to your question. It's trivially, obviously wrong to have separate classes to represent a CollectionOfFoo and a CollectionOfBar, so that can't be what you're asking.
You'll probably have to provide an example for each point of view, but you'll still probably get closed for being subjective.
避免代码重复总是一件好事。您需要警惕的一件事是过早泛化。
当您获得几段看起来相似的代码时,无需急于介入。在开发出一个好的模型之前,可以先坐下来。
即使举几个你需要概括的例子也可以,特别是当事情稍微复杂和/或开放式的时候。当你有 3-5 个以上的例子而不是一两个例子时,就更容易概括。
Avoiding code duplication is always a good thing. The one thing that you need to guard against is Premature Generalization.
There's no need to rush in a soon as you get a couple of pieces of code that look similar. Its OK to sit back until you have developed a good model.
Even letting a few examples of the thing you need to generalize can be OK, particularly, when things are a little more complex and/or open ended. It can be easier to generalize when you've got 3-5+ examples, rather than just one or two.
这是一个判断。大多数程序员重复代码太多,我认为这导致了热情的开发人员的态度,即消除重复绝对是一件好事,但事实并非如此。让代码易于阅读应该是首要任务,消除重复代码通常对可读性来说是件好事,但并非总是如此。
另外,我不会使用具有商业价值的代码来使用不熟悉的语言功能来学习它们。为此目的创建单独的学习项目。您不想最终在下班时间被叫去工作来修复由于过于喜欢泛型或任何其他功能而导致的错误。
This is a judgement call. Most programmers duplicate code too much, and I think that leads to the attitude among passionate developers that stamping out duplication is an absolute good, but it is not. Making your code easy to read should be the priority, and eliminating duplicate code is usually a good thing for readability, but not always.
Also, I wouldn't use commercially valuable code as a place to use unfamiliar language features for the purpose of learning them. Create separate learning projects for that purpose. You don't want to end up getting called into work on off-hours to fix bugs caused by getting too fancy with generics, or any other feature.
不可以。这两种情况都是不可接受的。使用泛型编写它,但只需达到所需的复杂程度即可。
不要重复代码;你必须双重修复错误、双重添加增强、双重编写注释、双重编写测试。只要您在该代码库上工作,您创建的每一行代码都是一个小小的负担;尽量减少你的负担。
No. Neither of those situations is acceptable. Write it with generics, but only as complex as it needs to be.
Don't duplicate code; you will have to double-fix bugs, double-add enhancements, double-write comments, double-write tests. Every line of code you create is a small burden you will have to carry for as long as you work on that codebase; minimize your burden.
取决于多种因素:
所以我的答案也许是……
Depends on a number of factors:
So my answer is maybe...
在给出答案之前,我想看一些示例代码,以了解问题的真正含义。
在等待的过程中,我相信,如果您的代码是按照最理智的 OO 原则构建的(例如每个类只做一件事并且只做一件事),就不应该有任何代码重复。当然有可能对抽象等感到疯狂,这最终会创建大量无用的类,但我认为这不是这里的问题。
Before giving my answer I'd like to see some example code to see what the question really is.
While waiting for that, I believe that if your code is built in the most sane OO principles (such as each class does only one thing and one thing only) there shouldn't be any code duplication around. It certainly is possible to go nuts with abstractions etc. which do end up creating a huge pile of useless classes but I don't think that's the issue here.
泛型难以阅读的主要原因是没有经验的程序员不熟悉它。这意味着在选择命名和文档时必须非常小心,以便清晰可见。
此类核心类的命名非常重要。设计师可能希望在选择名称之前与同行进行彻底的讨论。
The primary reason why generics are hard to read is because it is unfamiliar to unexperienced programmers. This implies that great care must be taken in choice of naming and documentation in order to make clarity shine through.
It is extremely important that such core classes are well-named. The designer may want to discuss this thoroughly with peers before choosing the names.
很好的问题;一般答案可能不适合您的情况。有很多因素决定您的选择
这些是决定您选择的一些关键因素。根据我的经验,我意识到重复的业务逻辑(中间层)代码不是一个好的做法,但表示层可以有重复的代码。
当我写这篇文章时,我记得文章“缓慢增长等于死亡”。编写高质量和非重复的代码可能需要时间,但这不应该成为您业务的瓶颈。
Excellent question; the generic answers may not fit for your situation. There are many factors decides your choice
These are some of the crucial factors decides your choice. In my experience I realize duplicate business logic (middle tier) code is not good practice but the presentation layer can have duplicate codes.
When I am writing this I remember the article "Does Slow Growth Equal Death". Writing quality and non-duplicate code might take time but that shouldn't be a bottleneck for your business.
通常,重复代码的唯一原因是克服语言的弱模板/宏/泛型系统或进行优化,其中存在处理不同类型的不同低级函数名称。
在 C++ 中,您注意到您不使用它,模板系统允许零开销类型和函数生成,并且专门化可用于“自定义”生成的代码某些类型参数。 Java 没有这样的东西,消除了 C++ 中可用的通用和特定选择避免。
在 Common Lisp 中,可以使用宏和编译器宏来生成类似于 C++ 模板的代码,其中可以针对特定类型自定义基本情况,从而产生不同的代码扩展。
在 Java 中,我唯一反对泛型代码的情况是使用数字类型的内部循环。在这些情况下,为泛型代码中的数字类型支付装箱和拆箱成本是不可接受的,除非您的探查器可以让您相信编译器或 HotSpot 足够聪明,可以放弃装箱。
至于可读性,那么,让你的代码成为你的同事必须面对的模型和挑战。为那些阅读困难的人提供教育。鼓励他们检查是否存在缺陷。如果他们找到任何版本,您就可以展示只需修复唯一版本的好处。
Usually the only reasons to duplicate code are to overcome a language's weak template/macro/generics system or for optimization, where there are different low-level function names that handle different types.
In C++, which you noted you don't work within, the template system allows zero-overhead type and function generation, with specialization available to "customize" the generated code for certain type parameters. Java has nothing of the sort, removing the both-general-and-specific choice avoidance available within C++.
In Common Lisp, one can employ macros and compiler macros to produce code similar to C++ templates, where basic cases can be customized for specific types, yielding different code expansions.
In Java, the only time I'd argue against generic code is for inner loops using numeric types. Paying the boxing and unboxing costs for numeric types in generic code is unacceptable in those cases, unless your profiler can convince you that the compiler or HotSpot was clever enough to forgo the boxing.
As for readability, well, make your code the model and the challenge to which your coworkers have to rise. Offer to educate those that have trouble reading it. Encourage them to review it for defects. If they find any, you can then demonstrate the benefit of only having to fix the one and only version.
将其与关系数据库中的规范化进行比较 - 您可能想要一个地方存放某种函数或数据生活,地方不多。它在可维护性和推理代码的能力方面有很大的不同。
Compare it to normalization in relational databases - you probably want one place where some kind of function or data lives, not many places. It makes a lot of difference in terms of maintainability and the ability to reason about your code.