Ich entwerfe eine Schnittstelle mit zwei ähnlichen Methoden:
public interface ThingComputer {
default Thing computeFirstThing() {
return computeAllThings().get(0);
}
default List<Thing> computeAllThings() {
return ImmutableList.of(computeFirstThing());
}
}
Etwa die Hälfte der Implementierungen berechnet immer nur eine Sache, während die andere Hälfte möglicherweise mehr berechnet.
Hat dies einen Präzedenzfall in weit verbreitetem Java 8-Code? Ich weiß, dass Haskell in einigen Schriftklassen ( Eq
zum Beispiel) ähnliche Dinge tut .
Der Vorteil ist, dass ich deutlich weniger Code schreiben muss als mit zwei abstrakten Klassen ( SingleThingComputer
und MultipleThingComputer
).
Der Nachteil ist, dass eine leere Implementierung kompiliert wird, aber zur Laufzeit mit einem explodiert StackOverflowError
. Es ist möglich, die gegenseitige Rekursion mit einem zu erkennen ThreadLocal
und einen schöneren Fehler zu melden, aber dies erhöht den Aufwand für nicht fehlerhaften Code.
quelle
throw new Error();
oder etwas Dummes, nur dass die Schnittstelle selbst keinen spröden Vertrag überdefault
Methoden haben sollte.abstract
, um sie zu einer Lösung zu zwingen .Antworten:
TL; DR: Tu das nicht.
Was Sie hier zeigen, ist spröder Code.
Eine Schnittstelle ist ein Vertrag. Es heißt: "Unabhängig davon, welches Objekt Sie erhalten, kann es X und Y ausführen." Wie bereits geschrieben, funktioniert Ihre Schnittstelle weder mit X noch mit Y, da garantiert wird , dass ein Stapelüberlauf auftritt.
Das Auslösen eines Fehlers oder einer Unterklasse weist auf einen schwerwiegenden Fehler hin, der nicht abgefangen werden sollte:
Darüber hinaus VirtualMachineError , die Elternklasse von Stackoverflow , sagt dieser:
Ihr Programm sollte sich nicht mit JVM-Ressourcen befassen . Das ist die Aufgabe der JVM. Das Erstellen eines Programms, das im Rahmen des normalen Betriebs einen JVM-Fehler verursacht, ist fehlerhaft. Entweder wird garantiert, dass Ihr Programm abstürzt, oder die Benutzer dieser Schnittstelle werden gezwungen, Fehler abzufangen, mit denen sie sich nicht befassen sollten.
Vielleicht kennen Sie Eric Lippert von solchen Bemühungen als emeritiertes "Mitglied des C # -Sprachdesign-Komitees". Er spricht über Sprachmerkmale, die Menschen zum Erfolg oder Misserfolg bewegen: Während dies kein Sprachmerkmal oder Teil des Sprachdesigns ist, gilt sein Standpunkt ebenso für die Implementierung von Schnittstellen oder die Verwendung von Objekten.
Quelle: C ++ und die Grube der Verzweiflung
Wenn ein Interface
StackOverflowError
standardmäßig einen wirft, werden die Entwickler in die Grube der Verzweiflung gedrängt und es ist eine schlechte Idee . Schieben Sie die Entwickler stattdessen in Richtung Pit of Success . Machen Sie es einfach Ihre Schnittstelle richtig zu verwenden und ohne die JVM abstürzt.Das Delegieren zwischen den Methoden ist hier in Ordnung. Die Abhängigkeit sollte jedoch in eine Richtung gehen. Ich stelle mir Methodendelegation gerne als gerichteten Graphen vor . Jede Methode ist ein Knoten im Diagramm. Zeichnen Sie jedes Mal, wenn eine Methode eine andere Methode aufruft, eine Kante von der aufrufenden Methode zu der aufgerufenen Methode.
Wenn Sie ein Diagramm zeichnen und feststellen, dass es zyklisch ist, ist das ein Codegeruch. Das ist ein Potenzial, um Entwickler in die Grube der Verzweiflung zu drängen. Beachten Sie, dass es nicht grundsätzlich verboten sein sollte, nur dass man Vorsicht walten lassen muss . Rekursive Algorithmen haben speziell Zyklen im Aufrufdiagramm: das ist in Ordnung. Dokumentieren Sie es und warnen Sie die Entwickler. Wenn es nicht rekursiv ist, versuchen Sie, diesen Zyklus zu unterbrechen. Wenn dies nicht möglich ist, ermitteln Sie, welche Eingaben einen Stapelüberlauf verursachen können, und verringern Sie diese oder dokumentieren Sie sie als letzten Fall, wenn sonst nichts funktioniert.
quelle