Очень повторяющийся код, как правило, плохо, и существуют шаблоны проектирования, которые могут помочь минимизировать это. Однако иногда это просто неизбежно из-за ограничений самого языка. Возьмите следующий пример из 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-кода очень нетипична, но даже при наилучшей практике повторение действительно происходит ! Рефакторинг не всегда работает, потому что это не всегда возможно (очевидный случай, когда повторение в документации).
Очевидно, что поддержание этого исходного кода - кошмар. Небольшая опечатка в документации или небольшая ошибка в реализации умножается на то, сколько раз было сделано повторений. На самом деле, лучшим примером является именно этот класс:
Блог исследований Google - Дополнительное, Дополнительное - Читать все об этом: почти все бинарные поиски и слияния нарушены (Джошуа Блох, инженер-программист)
Ошибка удивительно тонкая, возникающая из-за того, что многие считали простым и понятным алгоритмом.
// int mid =(low + high) / 2; // the bug
int mid = (low + high) >>> 1; // the fix
Вышеуказанная строка появляется 11 раз в исходном коде !
Итак, мои вопросы:
- Как на практике обрабатываются эти виды повторяющегося кода / документации Java? Как они разработаны, поддерживаются и проверяются?
- Вы начинаете с "оригинала" и делаете его как можно более зрелым, а затем копируете и вставляете по мере необходимости и надеетесь, что не ошиблись?
- А если вы допустили ошибку в оригинале, то просто исправьте ее повсюду, если вы не можете удалить копии и повторить весь процесс репликации?
- И вы применяете этот же процесс и для кода тестирования?
- Пользуется ли Java какой-либо предварительной обработкой исходного кода с ограниченным использованием для такого рода вещей?
- Возможно, у Sun есть собственный препроцессор, помогающий писать, поддерживать, документировать и тестировать этот тип повторяющегося библиотечного кода?
В комментарии был запрошен еще один пример, поэтому я извлек его из коллекции Google: com.google.common.base.Predicates строки 276-310 (AndPredicate
) против строк 312-346 (* 1058) *).
Источник для этих двух классов идентичен, за исключением:
AndPredicate
против OrPredicate
(каждый появляется 5 раз в своем классе)
"And("
против Or("
(в соответствующих toString()
методах)
#and
против #or
(в комментариях @see
Javadoc)
true
против false
(в apply
; !
можно переписать из выражения)
-1 /* all bits on */
против 0 /* all bits off */
в hashCode()
&=
против |=
в hashCode()