高度重复的代码通常是一件坏事,有一些设计模式可以帮助最小化这种情况。然而,有时由于语言本身的限制,这是不可避免的。以java.util.Arrays
的以下示例为例
/**
* Assigns the specified long value to each element of the specified
* range of the specified array of longs. The range to be filled
* extends from index <tt>fromIndex</tt>, inclusive, to index
* <tt>toIndex</tt>, exclusive. (If <tt>fromIndex==toIndex</tt>, the
* range to be filled is empty.)
*
* @param a the array to be filled
* @param fromIndex the index of the first element (inclusive) to be
* filled with the specified value
* @param toIndex the index of the last element (exclusive) to be
* filled with the specified value
* @param val the value to be stored in all elements of the array
* @throws IllegalArgumentException if <tt>fromIndex > toIndex</tt>
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex < 0</tt> or
* <tt>toIndex > a.length</tt>
*/
public static void fill(long[] a, int fromIndex, int toIndex, long val) {
rangeCheck(a.length, fromIndex, toIndex);
for (int i=fromIndex; i<toIndex; i++)
a[i] = val;
}
上述代码片段在源代码中出现了8次,文档/方法签名几乎没有变化,但方法主体完全相同,分别对应于int[]
、short[]
、char[]
、byte[]
、boolean[]
、double[]
、float[]
和Object[]
根数组类型。
我相信,除非一个人求助于反思(这本身是一个完全不同的主题),否则这种重复是不可避免的。我知道作为一个实用类,如此高度集中的重复性Java代码是非常不典型的,但即使是在最佳实践中,也会发生重复!重构并不总是有效的,因为它并不总是可行的(最明显的情况是在文档中出现重复的情况)。
显然,维护这些源代码是一场噩梦。文档中的一个小的打字错误,或者实现中的一个小错误,都会被重复的次数成倍增加。实际上,最好的例子恰好涉及到这个类:
这是一个令人惊讶的微妙错误,发生在许多人认为只是一个简单和直接的算法。
// int mid =(low + high) / 2; // the bug
int mid = (low + high) >>> 1; // the fix
在源代码!中,上面的行出现了11次!
所以我的问题是:
相同的过程
对于这类事情,
一个评论需要另一个例子,所以我从谷歌集合中提取了这个例子:com.google.common.base.Predicates第276-310行(AndPredicate
) vs第312-346行(OrPredicate
)。
这两个类的源代码是相同的,除了:
AndPredicate
vs OrPredicate
(每一个都在其class)"And("
vs代码中出现5次(在相应的代码methods)#and
vs #or
中(在@see
Javadoc expression)-1 /* all bits on */
vs apply
中;可以在hashCode()
中重写出OrPredicate
vs Or("
中的toString()
vs !
发布于 2010-02-26 12:06:52
对于绝对需要性能的人来说,装箱、拆箱和泛化集合等都是大忌。
同样的问题也发生在性能计算中,你需要相同的复杂结构才能同时处理浮点数和双精度浮点数(比如Goldberd的"“论文中展示的一些方法)。
这就是为什么在处理类似数量的数据时,Trove的TIntIntHashMap
会绕过HashMap<Integer,Integer>
。
那么Trove集合的源代码是如何编写的呢?
当然,通过使用源代码插装:)
有几个Java库可以获得更高的性能(远高于默认的Java库),它们使用代码生成器来创建重复的源代码。
我们都知道“源代码插装”是邪恶的,代码生成是垃圾,但这仍然是那些真正知道自己在做什么的人(即编写像Trove这样的东西的人)所做的事情:)
值得注意的是,我们生成的源代码包含大量警告,例如:
/*
* This .java source file has been auto-generated from the template xxxxx
*
* DO NOT MODIFY THIS FILE FOR IT SHALL GET OVERWRITTEN
*
*/
发布于 2010-02-26 04:10:52
如果您一定要复制代码,请遵循您给出的很好的示例,并将所有代码分组在一个地方,这样当您必须进行更改时,就很容易找到和修复这些代码。记录复制,更重要的是,记录复制的原因,以便在您之后的每个人都能意识到这两点。
发布于 2010-02-26 04:20:26
来自Wikipedia不要重复自己(DRY)或复制是邪恶的(DIE)
在某些情况下,强制执行DRY哲学所需的努力可能比维护单独的数据副本的努力更大。在其他一些上下文中,复制的信息是不可变的,或者受到足够严密的控制,以使DRY不是必需的。
可能没有答案或技术来防止类似的问题。
https://stackoverflow.com/questions/2337170
复制相似问题