使用Java管理高度重复的代码和文档

pol*_*nts 70 java maintenance preprocessor guava

高度重复的代码通常是一件坏事,并且有一些设计模式可以帮助减少这种情况.然而,由于语言本身的限制,有时它是不可避免的.以下示例来自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 &gt; toIndex</tt>
 * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
 *         <tt>toIndex &gt; 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;
}
Run Code Online (Sandbox Code Playgroud)

上面的片段出现在源代码的8倍,与文档/方法签名非常小的变化,但完全一样的方法体,一个用于每个根数组类型int[],short[],char[],byte[],boolean[],double[],float[],和Object[].

我相信除非有人采用反思(这本身就是一个完全不同的主题),否则这种重复是不可避免的.据我所知,作为一个实用类,如此高度集中的重复Java代码非常不典型,但即使采用最佳实践,也会发生重复!重构并不总是有效,因为它并不总是可行的(显而易见的情况是重复在文档中).

显然维护这个源代码是一场噩梦.文档中的轻微拼写错误或实现中的小错误会成倍增加,但会进行多次重复.事实上,最好的例子碰巧涉及这个确切的类:

谷歌研究博客 - 额外,额外 - 阅读所有关于它:几乎所有二进制搜索和合并都是破碎的(软件工程师Joshua Bloch)

错误是一个令人惊讶的微妙的错误,发生在许多人认为只是一个简单而直接的算法.

    // int mid =(low + high) / 2; // the bug
    int mid = (low + high) >>> 1; // the fix
Run Code Online (Sandbox Code Playgroud)

上面的代码在源代码中出现了11次!

所以我的问题是:

  • 如何在实践中处理这些重复的Java代码/文档?它们是如何开发,维护和测试的?
    • 你是从"原创"开始,尽可能地让它成熟,然后根据需要复制粘贴,希望你没有犯错?
    • 如果你确实在原版中犯了错误,那么只需在任何地方修复它,除非你习惯于删除副本并重复整个复制过程?
    • 您是否也为测试代码应用了相同的过程?
  • Java会从某种有限使用的源代码预处理中获益吗?
    • 也许Sun有自己的预处理器来帮助编写,维护,记录和测试这些重复的库代码?

评论请求了另一个示例,因此我从Google Collections中删除了这个:com.google.common.base.Predicates行276-310(AndPredicate)与第312-346行(OrPredicate).

这两个类的来源是相同的,除了:

  • AndPredicatevs OrPredicate(每个在同类中出现5次)
  • "And("vs Or("(在各自的toString()方法中)
  • #andvs #or(在@seeJavadoc评论中)
  • truevs false(in apply; !可以从表达式中重写)
  • -1 /* all bits on */vs 0 /* all bits off */inhashCode()
  • &=vs |=inhashCode()

Syn*_*r0r 32

对于那些绝对需要表演的人来说,装箱和拆箱以及一般化的收藏品都是不可能的.

同样的问题发生在哪里,你需要同样复杂,既为float和double工作高性能计算(比如一些Goldberd的显示方法" 是什么每台计算机科学家应该知道浮点数 "纸).

还有,为什么一个原因特罗韦TIntIntHashMap运行围绕Java的圈子中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
 * 
 */
Run Code Online (Sandbox Code Playgroud)


Bil*_*ard 16

如果您绝对必须复制代码,请按照您提供的优秀示例进行操作,并将所有代码分组到一个易于查找和修复的位置,以便进行更改.记录重复,更重要的是,记录重复的原因,以便跟在你后面的每个人都知道这两者.


sta*_*ker 6

来自维基百科不要重复自己(干)或复制是邪恶的(DIE)

在某些情况下,强制执行DRY理念所需的工作量可能大于维护数据的单独副本的工作量.在其他一些情况下,重复的信息是不可变的,或者保持在足够严格的控制之下,以使DRY不被要求.

可能没有答案或技术来防止这样的问题.