Un code très répétitif est généralement une mauvaise chose, et il existe des modèles de conception qui peuvent aider à minimiser ce phénomène. Cependant, il est parfois tout simplement inévitable en raison des contraintes du langage lui-même. Prenons l'exemple suivant, tiré de 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;
}
L'extrait ci-dessus apparaît 8 fois dans le code source, avec très peu de variation dans la documentation/signature de méthode mais exactement le même corps de méthode un pour chacun des types de tableaux Root. int[]
, short[]
, char[]
, byte[]
, boolean[]
, double[]
, float[]
et Object[]
.
Je crois qu'à moins de recourir à la réflexion (ce qui est un tout autre sujet en soi), cette répétition est inévitable. Je comprends qu'en tant que classe utilitaire, une telle concentration de code Java répétitif est très atypique, mais même avec les meilleures pratiques, la répétition se produit ! Le refactoring ne fonctionne pas toujours car ce n'est pas toujours possible (le cas évident est lorsque la répétition se trouve dans la documentation).
Il est évident que la maintenance de ce code source est un cauchemar. Une légère coquille dans la documentation, ou un bug mineur dans l'implémentation, est multiplié par le nombre de répétitions. En fait, le meilleur exemple se trouve être cette classe exacte :
Le bogue est étonnamment subtil et se produit dans ce que beaucoup pensaient être un algorithme simple et direct.
// int mid =(low + high) / 2; // the bug
int mid = (low + high) >>> 1; // the fix
La ligne ci-dessus apparaît 11 fois dans le code source !
Mes questions sont donc les suivantes :
- Comment ces types de code/documentation Java répétitifs sont-ils traités dans la pratique ? Comment sont-ils développés, maintenus et testés ?
- Faut-il commencer par "l'original", et le rendre aussi mature que possible, puis copier et coller si nécessaire et espérer ne pas avoir fait d'erreur ?
- Et si vous avez fait une erreur dans l'original, il suffit de la corriger partout, à moins que vous ne soyez à l'aise avec la suppression des copies et la répétition de tout le processus de réplication ?
- Et vous appliquez ce même processus pour le code de test également ?
- Java bénéficierait-il d'une sorte de prétraitement du code source à usage limité pour ce genre de choses ?
- Peut-être Sun a-t-il son propre préprocesseur pour aider à écrire, maintenir, documenter et tester ce genre de code de bibliothèque répétitif ?
Un commentaire demandait un autre exemple, j'ai donc tiré celui-ci de Google Collections : com.google.common.base.Predicates lignes 276-310 ( AndPredicate
) contre les lignes 312-346 ( OrPredicate
).
La source de ces deux classes est identique, à l'exception de :
-
AndPredicate
vsOrPredicate
(chacun apparaît 5 fois dans sa classe) -
"And("
vsOr("
(dans leurstoString()
méthodes) -
#and
vs#or
(dans le@see
Commentaires de la Javadoc) -
true
vsfalse
(enapply
;!
peut être réécrit hors de l'expression) -
-1 /* all bits on */
vs0 /* all bits off */
surhashCode()
-
&=
vs|=
surhashCode()
11 votes
Il semble que vous soyez particulièrement préoccupé par les répétitions dues au code de manipulation des tableaux primitifs. Personnellement, j'évite ce genre de répétition (et j'encourage les autres à faire de même) en utilisant des collections génériques et l'autoboxing, en évitant les tableaux et les primitives sauf en cas de nécessité absolue. Avez-vous des exemples de cette répétition qui Ne le fais pas. impliquent des tableaux primitifs ?
2 votes
+1 pour ce bon article et netlib.bell-labs.com/cm/cs/pearls
0 votes
La répétitivité due à la fourniture de surcharges complètes n'est qu'un exemple. J'ai vu ce genre de répétition dans des scénarios de gestion de tableaux non surchargés et non primitifs également.
2 votes
J'ai jeté un coup d'oeil à la classe java.util.Arrays et j'ai trouvé l'extrait de javadoc suivant assez amusant : "Le code pour chacun des sept types primitifs est largement identique. C'est la vie".
0 votes
La bibliothèque Java de Sun/Oracle utilise un système de modèle manquant pour les tampons NIO.
0 votes
Ce serait bien si Java avait les macros hygiéniques de Scheme.