Nehmen wir an, ich habe Door
s, die von a verwaltet werden DoorService
. Der DoorService
ist für das Öffnen, Schließen und Verriegeln der in der Datenbank gespeicherten Türen zuständig.
public interface DoorService
{
void open(Door door) throws DoorLockedException, DoorAlreadyOpenedException;
void close(Door door) throws DoorAlreadyClosedException;
/**
* Closes the door if open
*/
void lock(Door door) throws DoorAlreadyLockedException;
}
Für die Sperrmethode gibt es eine DoorAlreadyLockedException
und für die offene Methode gibt es eine DoorLockedException
. Dies ist eine Option, es sind jedoch auch andere Optionen möglich:
1) Verwendung DoorLockedException
für alles, ist etwas umständlich, wenn die Ausnahme bei einem lock()
Anruf abgefangen wird
try
{
doorService.lock(myDoor);
}
catch(DoorLockedException ex) // door ALREADY locked
{
//error handling...
}
2) Haben Sie die 2 Ausnahmetypen DoorLockedException
undDoorAlreadyLockedException
3) Haben Sie die 2 Ausnahmetypen aber lassen DoorAlreadyLockedException extends DoorLockedException
Welches ist die beste Lösung und warum?
java
exceptions
class-design
Winter
quelle
quelle
Antworten:
Ich weiß, dass die Leute viel damit anfangen, Ausnahmen für die Flusskontrolle zu verwenden, aber das ist hier nicht das größte Problem. Ich sehe eine große Verletzung bei der Trennung von Befehlsabfragen . Aber auch das ist nicht das Schlimmste.
Nein, das Schlimmste ist genau hier:
Warum zum Teufel explodiert alles andere, wenn Ihre Annahme über den Türzustand falsch ist, es aber
lock()
nur für Sie behebt? Vergessen Sie, dass dies das Öffnen der Tür unmöglich macht, was absolut möglich und gelegentlich nützlich ist. Nein, das Problem hier ist, dass Sie zwei verschiedene Philosophien gemischt haben, um mit falschen Annahmen umzugehen. Das ist verwirrend. Tu das nicht. Nicht auf derselben Abstraktionsebene mit demselben Namensstil. Au! Nehmen Sie eine dieser Ideen mit nach draußen. Die Türservice-Methoden sollten alle gleich funktionieren.Was die Verletzung der Befehlsabfragetrennung betrifft, sollte ich nicht versuchen müssen, eine Tür zu schließen, um herauszufinden, ob sie geschlossen oder offen ist. Ich sollte nur fragen können. Der Türservice bietet keine Möglichkeit, dies zu tun, ohne möglicherweise den Zustand der Tür zu ändern. Das macht dies so viel schlimmer als Befehle, die auch Werte zurückgeben (ein häufiges Missverständnis dessen, worum es bei CQS geht). Hier sind Zustandsänderungsbefehle die einzige Möglichkeit, Abfragen durchzuführen! Au!
Ausnahmen, die teurer sind als Statuscodes, sind Optimierungsgespräche. Schnell genug ist schnell genug. Nein, das eigentliche Problem ist, dass Menschen in typischen Fällen keine Ausnahmen erwarten. Sie können darüber streiten, was typisch ist, was Sie wollen. Für mich ist die große Frage, wie lesbar Sie den Verwendungscode machen.
Bitte lassen Sie mich keinen solchen Code schreiben. Bitte geben Sie uns
isLocked()
undisClosed()
Methoden. Mit denen kann ich meine eigenenensureClosed()
undensureUnlocked()
leicht lesbaren Methoden schreiben . Diejenigen, die nur werfen, wenn ihre Postbedingungen verletzt werden. Ich würde lieber nur feststellen, dass Sie sie natürlich bereits geschrieben und getestet haben. Mischen Sie sie nur nicht mit denen, die werfen, wenn sie den Zustand nicht ändern können. Geben Sie ihnen zumindest unterscheidende Namen.Was auch immer Sie tun, rufen Sie bitte nichts an
tryClose()
. Das ist ein schrecklicher Name.Was das
DoorLockedException
Alleinsein betrifft, mussDoorAlreadyLockedException
ich auch sagen: Es geht nur um die Verwendung von Code. Entwerfen Sie solche Dienste nicht, ohne den verwendeten Code zu schreiben und sich das Chaos anzusehen, das Sie erstellen. Refactor und Redesign, bis der verwendete Code mindestens lesbar ist. In der Tat sollten Sie zuerst den Verwendungscode schreiben.quelle
lock()
Methode, die aufgerufen wird,close()
war nur, dass ich beim Schreiben der Frage faul war, aber danke, dass Sie mich darauf aufmerksam gemacht haben! Ich werde nicht darauf hereinfallen, wenn ich echten Code schreibe. In meinem Fall werden die 3 Methoden nie wirklich nacheinander aufgerufen. Es ist nur: Rufen Sie eine an, wenn es keine gute Ausnahme gibt, andernfalls zeigen Sie einen übersetzten Dialog und melden Sie sich möglicherweise ab (wenn InvalidTokenException). Ich habeisLocked()
undisOpen()
Methoden und sie werden im Kontrollfluss des Servers verwendet, aber sie sollten nicht verwendet werden, um zu überprüfen, ob ein Fehlerdialog angezeigt werden soll, das ist ein Job für eine Ausnahme.DoorAlreadyLockedException
, macht aber Zwillingsklassen.public class DoorAlreadyLockedException inherits DoorLockedException {}
. Definieren Sie eine Klasse in einer Zeile, um ihr einen guten Namen zu geben. Wählen Sie mit Bedacht aus, was Sie erben. Ich ziehe es vor, von so hoch wie möglich zu erben, obwohl ich nicht sinnlos überflüssig bin, um eine lange Vererbungskette zu vermeiden.NoOneGaveMeAKey
,NoOneGaveMeAKeyException
nur um pendantisch zu sein. Ansonsten stimme ich dem Hauptpunkt zu, dass wir vor der Verarbeitung in der Lage sein müssen, den Zustand des Objekts zu kennen.isOpen
/isClosed
ist nicht gut genug. Denken Sie an das Dateisystem. Sie können überprüfen, ob die Datei vorhanden ist oderIOException
nicht. Dies kann sich jedoch ändern, wenn Sie versuchen, sie zu lesen oder zu schreiben . Vielleicht besteht in diesem Zusammenhang wirklich die Möglichkeit, dass der Status ungültig ist, wenn Sie die Tür öffnen oder schließen.Eine Unterscheidung zwischen
DoorLockedException
undDoorAlreadyLockedException
legt nahe, dass verschiedene Ausnahmetypen für die Flusskontrolle verwendet werden, eine Praxis, die bereits allgemein als Antimuster angesehen wird.Es ist unentgeltlich, mehrere Ausnahmetypen für etwas zu haben, das so harmlos ist. Die zusätzliche Komplexität wird durch die zusätzlichen Vorteile nicht aufgewogen.
Einfach
DoorLockedException
für alles verwenden.Besser noch, einfach nichts tun. Wenn die Tür bereits verriegelt ist, befindet sie sich nach Abschluss der
lock()
Methode immer noch im richtigen Zustand . Wirf keine Ausnahmen für Bedingungen, die unauffällig sind.Wenn Ihnen das immer noch unangenehm ist, benennen Sie Ihre Methode um
ensureDoorLocked()
.quelle
DoorLockedException
undDoorAlreadyLockedException
schlage vor, dass es nur darum geht, einen viel aussagekräftigeren Fehlerbehandler für dielock()
Methode zu haben, auf Kosten einer fast doppelten Klasse.Das Thema dieser Frage "Sollten recycelte Ausnahmetypen den Einwegarten vorgezogen werden?" scheint ignoriert worden zu sein, nicht nur in den Antworten, sondern auch in den Details für die Fragen (die Antworten waren auf die Details der Frage).
Ich stimme der meisten Kritik an dem von den Befragten angebotenen Code zu.
Als Antwort auf die Überschriftenfrage würde ich jedoch sagen, dass Sie die Wiederverwendung von "recycelten Ausnahmen" gegenüber "Einwegausnahmen" nachdrücklich vorziehen sollten.
Bei einer typischeren Verwendung der Ausnahmebehandlung haben Sie einen großen Abstand im Code zwischen dem Ort, an dem die Ausnahme erkannt und ausgelöst wird, und dem Ort, an dem die Ausnahme abgefangen und behandelt wird. Das bedeutet, dass die Verwendung privater (modularer) Typen bei der Ausnahmebehandlung im Allgemeinen nicht gut funktioniert. Ein typisches Programm mit Ausnahmebehandlung - enthält eine Handvoll Speicherorte der obersten Ebene im Code, an denen Ausnahmen behandelt werden. Und da es sich um Top-Level-Standorte handelt, ist es für sie schwierig, detaillierte Kenntnisse über enge Aspekte bestimmter Module zu haben.
Stattdessen werden sie wahrscheinlich einen High-Level-Handler für ein paar High-Level-Probleme haben.
Der einzige Grund, warum die Verwendung detaillierter benutzerdefinierter Ausnahmeklassen in Ihrem Beispiel sinnvoll erscheint, liegt darin, dass Sie (und hier die anderen Befragten nachzeichnen) nicht die Ausnahmebehandlung für den normalen Kontrollfluss verwenden sollten.
quelle
Eine unerforschte Alternative. Möglicherweise modellieren Sie mit Ihren Klassen das Falsche. Was wäre, wenn Sie diese stattdessen hätten?
Jetzt ist es unmöglich, etwas zu versuchen, das ein Fehler ist. Sie brauchen keine Ausnahmen.
quelle
Ich werde die ursprüngliche Frage beantworten, anstatt das Beispiel zu kritisieren.
Einfach ausgedrückt, wenn Sie erwarten, dass der Client für jeden Fall anders reagieren muss, ist ein neuer Ausnahmetyp erforderlich.
Wenn Sie sich für die Verwendung mehrerer Ausnahmetypen entscheiden und dennoch erwarten, dass der Client in einigen Fällen eine gemeinsame catch-Klausel für diese erstellen möchte, sollten Sie wahrscheinlich eine abstrakte Oberklasse erstellen, die beide erweitern.
quelle
Dies ist ein Pseudocode, aber ich denke, Sie werden verstehen, was ich meine:
Ja, die Wiederverwendung von Ausnahmetypen ist sinnvoll, wenn Sie Ausnahmen für wirklich außergewöhnliche Dinge verwenden . Denn wirklich außergewöhnliche Dinge sind meistens Querschnittsthemen. Sie haben Ausnahmen hauptsächlich für Kontrollflüsse verwendet, und das führt zu diesen Problemen.
quelle
DoorStuckException
undHouseOnFireException
sollten nicht überprüft werden, sie sollen nicht bei allen Anrufen abgefangen werden.