Ich wollte nurmal fragen, ob es legitim ist, bestimmte Exceptions für den Programmablauf zu benutzen? Also konkret sowas hier:
rollChampion();
rollMasteries();
rollSpells();
rollBoots();
rollFirstItem();
rollSecondItem();
rollOtherItems();
rollFirstMax();
updateGUI();
} catch (ChampionNotFoundException ex) {
JOptionPane.showMessageDialog(this, "Der gesuchte Champion \"" + randomComboBox.getSelectedItem() + "\" wurde nicht gefunden. Bitte gültigen Champion eingeben!", "Champion \"" + randomComboBox.getSelectedItem() + "\" nicht gefunden", JOptionPane.ERROR_MESSAGE);
}
private void rollChampion() throws ChampionNotFoundException {
currentChampion = null;
if (!randomCheckBox.isSelected() && randomComboBox.getSelectedItem().equals("")) {
randomCheckBox.setSelected(true);
}
if (randomCheckBox.isSelected()) {
currentChampion = champions.get(r.nextInt(champions.size()));
randomComboBox.setSelectedIndex(0);
} else {
currentChampion = Champion.getChampion((String) randomComboBox.getSelectedItem());
if (currentChampion == null) {
throw new ChampionNotFoundException();
}
}
}```
Wenn der Benutzer nen Champion eingegeben hat, der nicht existiert fliegt die CHampionNotFOundException und in der ersten MEthode gibt er die Fehlermeldung aus und bricht alle anderen Methoden ab. ICh mein nur mal gelesen zu haben, das man sowas vermeiden soll, da der AUfrufer der Methode nicht wirklich was ändern kann, damit die nicht fliegt (kein Rückgabetyp und parameter), sondern nur der Benutzer.
Also kann man solche Stelen belassen, oder gibts ne andere Möglichkeit dafür?
je komplizierter der Programmfluss, desto eher werden Exceptions akzeptabel,
im Beispiel oben könnte rollChampion() auch boolean zurückgeben, nur eine Stelle höher greift darauf zu, dort das if/else,
das ist bereits in der Summe weniger als die Exception zu werfen + mühsames try/ catch,
die natürlichere Entscheidung,
mal abgesehen davon dass if/else generell der bessere Stil, keine Frage
aber wenn erstmal jede der 8 roll-Methoden eine weitere aufruft, bei der sich diese Frage stellt,
dann sind vielleicht if/ else in 8 Methoden nötig,
je komplexer das Modell mit mehr Methoden, mehr Aufruftiefen, desto eher wird man bei der Exception landen
unabhängig zur if/ else-Möglichkeit hinsichtlich der Art der Verwendung sieht es hier noch relativ akzeptabel aus als Ausnahme wegen fehlender User-Auswahl?
wenn aber normale Prozesse (wie als Beispiel Auftreten eines Kampfes für den Champions und Abbruch der Kampfschleife bei Leben 0 eines der Kontrahenten) standardmäßg mit Exception gesteuert, dann noch eine Stufe kritischer,
wobei theoretisch selbst dann noch vorstellbar, man muss es sich nur bewußt machen, Frage immer guter Anfang
Exceptiones sollten für Fehler/Ausnahmen genutzt werden. Für eine gewünschtes/oft auftretendes Verhalten sollte man sich lieber passende Rückgabewerte Ausdenken. Das ist leichter Verständlich, benötigt keine komplizierten try/catch sonst welche Behandlungen.
Das kann von boolean anstelle void sein (dann kann die Aufrufende Methode überlegen, ob sie das dann bearbeitet oder nicht) oder die Verwendung von ENUMS bis hin zu Komplexen Klassen.
Ich würde das hier auch als legitim ansehen. Eine Exception sollte dann auftreten, wenn der aktuelle Prozess durch eine Ausnahme gestört wird und dieser dadurch abgebrochen werden sollte. Einen neuen Prozess starten wie “Benutzer über eine fehlerhafte Eingabe informieren” ist vollkommen in Ordnung. Wenn man unsicher ist denke ich hilft es, wenn man sich bewusst macht, dass an der Stelle an der die Exception Auftritt der vorherige Code als abgeschlossen angesehen wird.
Ich bin Fan von try4j. Dort gibt es den Datentyp Try, der ähnlich wie Optional arbeitet, sich im Fehlerfall aber die Exception merken kann. Das Ganze lehnt sich stark an Scala an, und die haben das Konzept von Haskell (dort z.B. über Either realisiert) übernommen.
Die Idee ist, den Fehlerfall nicht separat und nicht-lokal vom Erfolgsfall zu bearbeiten, sondern “mitzuschleifen”, und so die Notwendigkeit einer Fehlerbehandlung schon im Rückgabetyp explizit zu machen (so wie Optional die Notwendigkeit zum “Null”-Check explizit macht).
Wenn ich eine “Composite Method” habe, und in einer (oder mehreren) der darin aufgerufenen Methoden ein Ereignis auftreten kann, dass dazu führt, dass die restlichen Methoden (>1) nicht mehr aufgerufen werden sollen, würde ich durchaus eine eigene CheckedException einsetzten.
Checked oder Unchecked?
die Welt mit Unchecked doch immer besser dran,
man denke sich immer der Fall der separaten, nicht zu berührenden API, die in einem Code durchlaufen werden soll,
als simplen Fall etwa Collections.sort():
eigener Code -> Aufruf Collections.sort() -> Aufruf der eigenen compareTo()-Methode
was wenn nun die compareTo()-Methode abbrechen will? in diesem Fall mag man sich das von verbitten, aber nur ein Beispiel für komplexere Fälle,
mit if und else kommt man jedenfalls nirgendwo hin, auf Collections.sort() keinen Einfluss, Checked Exceptions auch verboten,
komische Option/ Try-Rückgaben wäre Verschandelung der API
Unchecked Exception ist hier die ideale mächtige Möglichkeit des Rücksprungs,
nicht zu missbrauchen, aber schon sehr sehr nettes Werkzeug
[quote=SlaterB]die Welt mit Unchecked doch immer besser dran,[/quote]Dem OP gings explizit um Programmsteuerung per Exception und für diesen Fall sorgt eine unchecked Exception viel später für einige WTFs…
Für Echte Fehler, die ein sinnvolles Weiterführen des Programms (zumindest aus Sicht des Moduls) unmöglich machen sind unchecked Exceptions unverzichtbar.
Ich hab jetzt nur überflogen, aber alleine beim Thema “Exception zur Programmflusssteuerung” kam mir sofort “nope!” in den Kopf.
Grundsätzlich sagt es eigentlich das Wort Exception, zu Deutsch: Ausnahme, selbst: es ist im normalen Programmablauf ein Fehler aufgetreten der so nicht vorgesehen ist. Nun kann man sich darüber streiten wie sinnvoll manche davon sind und wie aufwändig ein Code ist der durch vorherige Prüfungen bestimmte Fehler vorher erkennt und an manchen Stellen entsprechende Konstrukte überflüssig macht (z.B. macht eine vorherige Prüfung ob eine Datei vorhanden und les-/schreibbar ist Fehler wie “nicht gefunden” obsolet - trotzdem kann während des Vorgangs ein Fehler wie Plattenausfall auftreten), aber es geht nicht ohne. Dies wiederum impliziert dass man für bestimmte Ausnahmen diese zur weiteren Steuerung nutzen muss, jedoch sollte man sein Design nicht gleich von Beginn an darauf ausrichten.
Auch muss man immer prüfen wer wie viel Schuld am Fehler hat: der Programmierer weil er sich drauf verlässt das der User keine Fehler bei der Dateneingabe macht, der User selbst weil er es nicht packt die fertige Anwendung richtig zu nutzen (Fehler 40/80) und einfach die Hardware weil im RAM mal wieder ein Bit umgekippt ist.
Letzlich bleibt wie so oft: an einigen Stellen gibt es keinen alternativen Weg “hinten rum”.
Exception-Verarbeitung soll ein Programm auch etwas langsamer machen, habe ich mal gelesen.
So lange es sich mit ordentlicher Programmstruktur umgehen lässt, sollten Exceptions den Programmfluss nicht steuern.
Schwieriger Satz - gerade für Anfänger. Ab wann hat man eine ordentliche Programmstruktur erreicht ;)? - So manche Exception lässt sich vermeiden durch erlauben von null als Rückgabewert. Was dann auch ganz gern in Kombination mit erlauben von null-werten als Parameter kombiniert wird. Man sollte sich aber gut überlegen, ob null wirklich so gut ist und ich glaube wenn mans mal gewohnt ist, wird es zu gerne zu inflationär genutzt. Man sollte sich auch überlegen, ob da nicht etwas schief gelaufen ist, wenn die Methode null zurück gibt - was dann eine Ausnahme wäre und eine Exception rechtfertigt. Die nächste Frage wäre dann eben wie schwer das ist. Behandelbar im Programm (z.B. durch falsche Eingabe des Nutzers) → Exception oder Fehler in der Programmierung → RuntimeException.
Generell finde auch ich, dass Ausnahmen nicht zur normalen Programmsteuerung benutzt werden sollten. Aber im gezeigten Code bin ich unsicher. Ist denn “Champion nicht gefunden” bei dir ein Fall, der durchaus auftreten kann und darf? Oder wäre das dann eine fehlerhafte Benutzereingabe oder sowas?
Ansonsten könntest du auch vor der ganzen Kaskade testen, ob der Champion da ist, je nachdem wie aufwändig das ist. Ggf. könnte der gefundene Champion dann auch übergeben werden.
Was hier kurz anklang, null als Rückgabewert sollte man vermeiden. Null als Parameter sollte man gleich noch viel mehr vermeiden, frei aus der Erinnerung nach Clean Code.
Champion nicht gefunden resultiert aus fehlerhafter Benutzereingabe. Und ich hätte das ganze auch mit if/else reaiisieren können, das wäre aber deutlich unübersichtlicher geworden
Fehlerhafte Benutzereingaben kann man zB. in der GUI abfangen bzw. verhindern, drop down box mit vordefinierten Werten anstatt Freitext waere zumindest eine theoretische Moeglichkeit.
Es ist ja ein Drop-down menü, allerdings kann man in dem suchen, da es 128 Champions gibt. Man kann also reintippen und übereinstimmende Champions werden angezeigt. Wählt er aus den gefundenen jedoch seinen unvollständigen Suchbegriff, fliegt die Exception und weißt ihn darauf hin
Hier ein Screen von dem Ausschnitt:
Hier ist “le” der Suchbegriff und der Rest die gefundenen Ergebnisse. Wählt er also hier “le” aus, fliegt die Exception.
[quote=Darse]Wählt er aus den gefundenen jedoch seinen unvollständigen Suchbegriff,[/quote]Da haben wir doch das Problem: Das was der User zum Suchen eingegeben hat sollte nicht in der Auswahl erscheinen.
Denkt doch mal in eine andere Richtung. Exceptions sind eine Art der Programmflusssteuerung und zwar eine, die in eine bestimmte Richtung, nämlich Richtung Fehlerbehandlung führen. Das bedeutet im Umkehrschluss, dass sie auch genau nur dies tun sollten… Den Programmfluss im Störungsfall steuern. Für Abfragen im normalen Programmablauf sind sie deswegen mMn ein absolutes NoGo.
Richtig, sie sollten nur zur Fehlerbehandlung da sein.
Wenn nun ein Benutzer einen ungültigen Wert eingibt, ist dessen Behandlung eine Fehlerbehandlung, nicht wahr?
Natürlich ist es vorzuziehen, wenn man diesen im Fall einer Gui schon vorher gar nicht erst zulässt. Aber das ist nicht immer möglich. Hier hingegen wohl schon.