Je conçois une interface avec deux méthodes connexes, similaires à ceci:
public interface ThingComputer {
default Thing computeFirstThing() {
return computeAllThings().get(0);
}
default List<Thing> computeAllThings() {
return ImmutableList.of(computeFirstThing());
}
}
Environ la moitié des implémentations ne calculeront jamais qu'une chose, tandis que l'autre moitié peut en calculer plus.
Cela a-t-il un précédent dans le code Java 8 largement utilisé? Je sais que Haskell fait des choses similaires dans certaines classes ( Eq
par exemple).
L'avantage est que je dois écrire beaucoup moins de code que si j'avais deux classes abstraites ( SingleThingComputer
et MultipleThingComputer
).
L'inconvénient est qu'une implémentation vide se compile mais explose au moment de l'exécution avec a StackOverflowError
. Il est possible de détecter la récursivité mutuelle avec a ThreadLocal
et de donner une erreur plus agréable, mais cela ajoute une surcharge au code non bogué.
la source
throw new Error();
ou quelque chose de stupide, seulement que l' interface elle-même ne devrait pas avoir un contrat fragile à travers lesdefault
méthodes.abstract
existent pour les forcer à le résoudre.Réponses:
TL; DR: ne faites pas cela.
Ce que vous montrez ici, c'est du code fragile.
Une interface est un contrat. Il dit "quel que soit l'objet que vous obtenez, il peut faire X et Y". Au moment où elle est écrite, votre interface ne fait ni X ni Y car elle est garantie de provoquer un débordement de pile.
Lancer une erreur ou une sous-classe indique une erreur grave qui ne devrait pas être interceptée:
De plus, VirtualMachineError , la classe parente de StackOverflowError , dit ceci:
Votre programme ne doit pas se préoccuper des ressources JVM . C'est le travail de la JVM. Faire un programme qui provoque une erreur JVM dans le cadre d'un fonctionnement normal est mauvais. Il garantit soit que votre programme plantera, soit oblige les utilisateurs de cette interface à intercepter les erreurs qui ne devraient pas le concerner.
Vous connaissez peut-être Eric Lippert grâce à des efforts comme émérite «membre du comité de conception du langage C #». Il parle de fonctionnalités linguistiques poussant les gens vers le succès ou l'échec: bien qu'il ne s'agisse pas d'une fonctionnalité linguistique ou d'une partie de la conception du langage, son argument est tout aussi valable lorsqu'il s'agit de mettre en œuvre des interfaces ou d'utiliser des objets.
Source: C ++ et la fosse du désespoir
Avoir une interface jette un
StackOverflowError
par défaut pousse les développeurs dans la fosse du désespoir et c'est une mauvaise idée . Poussez plutôt les développeurs vers la fosse du succès . Faites - facile à utiliser l' interface correctement et sans écraser la machine virtuelle Java.Déléguer entre les méthodes est bien ici. Cependant, la dépendance doit aller dans un sens. J'aime penser la délégation de méthode comme un graphique dirigé . Chaque méthode est un nœud sur le graphique. Chaque fois qu'une méthode appelle une autre méthode, tracez un bord entre la méthode appelante et la méthode appelée.
Si vous dessinez un graphique et remarquez qu'il est cyclique, c'est une odeur de code. C'est un potentiel pour pousser les développeurs dans la fosse du désespoir. Notez qu'il ne doit pas être catégoriquement interdit, seulement que l' on doit faire preuve de prudence . Les algorithmes récursifs auront spécifiquement des cycles dans le graphe d'appel: c'est très bien. Documentez-le et avertissez les développeurs. S'il n'est pas récursif, essayez de briser ce cycle. Si vous ne le pouvez pas, découvrez quelles entrées peuvent provoquer un débordement de pile et atténuez-les ou documentez-les comme dernier cas si rien d'autre ne fonctionne.
la source