在 Java 中管理高度重复的代码和文档

2022-08-31 13:52:53

高度重复的代码通常是一件坏事,并且有一些设计模式可以帮助最大限度地减少这种情况。但是,有时由于语言本身的限制,这是不可避免的。以下示例取自 :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;
}

上面的代码片段在源代码中出现了 8 次,文档/方法签名的变化很小,但方法主体完全相同,每个根数组类型 、 、 和 .int[]short[]char[]byte[]boolean[]double[]float[]Object[]

我认为,除非人们诉诸反思(这本身就是一个完全不同的主题),否则这种重复是不可避免的。我知道,作为一个实用程序类,如此高度集中的重复Java代码是非常不典型的,但即使使用最佳实践,重复也会发生!重构并不总是有效,因为它并不总是可能的(明显的情况是当重复在文档中时)。

显然,维护这个源代码是一场噩梦。文档中的轻微拼写错误或实现中的小错误会乘以重复次数。事实上,最好的例子恰好涉及这个确切的类:

Google Research Blog - Extra, Extra - Read All About It: 幾乎所有的二元搜索和Mergesorts都被打破了(作者:Joshua Bloch,軟件工程師)

这个错误是一个令人惊讶的微妙错误,发生在许多人认为只是一个简单明了的算法中。

    // int mid =(low + high) / 2; // the bug
    int mid = (low + high) >>> 1; // the fix

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

所以我的问题是:

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

一条评论要求另一个例子,所以我从Google Collections中提取了这个:com.google.common.base.谓词行276-310()与行312-346()。AndPredicateOrPredicate

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

  • AndPredicatevs(每个在同类产品中出现 5 次)OrPredicate
  • "And("vs(在各自的方法中)Or("toString()
  • #andvs (在 Javadoc 注释中)#or@see
  • truevs (在 ; 可以重写出来的表达式)falseapply!
  • -1 /* all bits on */与在0 /* all bits off */hashCode()
  • &=与在|=hashCode()

答案 1

对于绝对需要性能的人来说,拳击和拆箱以及通用的集合等等都是大禁忌。

同样的问题也发生在性能计算中,你需要相同的复合物来同时用于浮点数和双精度(比如Goldbard的每个计算机科学家都应该知道的浮点数论文中所示的一些方法)。

Trove's在处理类似数量的数据时围绕Java运行是有原因的。TIntIntHashMapHashMap<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
 * 
 */

答案 2

如果您绝对必须重复代码,请按照您给出的出色示例进行操作,并将所有这些代码分组到一个位置,以便在您必须进行更改时轻松查找和修复。记录重复,更重要的是,记录重复的原因,以便您之后的每个人都知道这两者。


推荐