# Wann und wie Exceptions einsetzen?



## -frank (23. Feb 2007)

ich bräuchte ein paar tipps zum einsatz von exceptions:
als beispiel nehme ich nen graphen mit punkten drauf und linien zwischen zwei punkten. mit addPoint() zeichnet man nen punkt auf dem graphen (addPoint(x,y) tut dasselbe, erzeugt aber vorher das MyPoint-Objekt). addLine() erzeugt ne Linie zwischen zwei bereits existierenden Punkten (ansonsten gibts ne Exception):


```
class MyPoint {
..
    MyPoint(int x, int y) throws IllegalPointParametersException; // zb negative koordinaten
    {..}
    ..
}

class Graph {
    void addPoint(MyPoint point) throws PointAlreadyExistsException;
    void addPoint(int x, int y) throws ?
    MyPoint getPoint(MyPoint point) throws NoSuchPointException();
    MyPoint getPoint(int x, int y) throws NoSuchPointException();

    void addLine(MyPoint p1, MyPoint p2) throws ?
    void addLine(int x1, y1, x2, y2) throws ?
    MyLine getLine(MyPoint p1, MyPoint p2) throws NoSuchLineException (auch NoSuchPointException?)
}
```

meine frage nun zb zu addPoint(x,y), welches das MyPoint-Objekt kreiert und danach addPoint(MyPoint) aufruft:
soll diese Methode nun beide Exceptions, also IllegalPointParametersException und PointAlreadyExistsException, werfen? oder sollte ich ne CannotCreatePointException machen, die dann eben eine der anderen beiden als nested exception hat?

getLine(p1,p2) liefert nur dann ne Linie, wenn diese existiert. wäre es nun die beste lösung, immer ne NoSuchLineException zu werfen oder NoSuchLineEx mit nested NoSuchPointEx oder soll ich die getLine Methode beide Exceptions werfen lassen?

bei addLine(p1,p2) ists dasselbe mit NoSuchPointException und LineAlreadyExistsEx.

bei addLine(x1,y1,x2,y2) gibts dann noch ein paar möglichkeiten mehr.

---

ich gehe mal stark davon aus, dass es auch auf das jeweilige programm ankommt, was man von weiter außen mit den exceptions anfangen kann, ob viele verschiedene programme den graph verwenden, etc.
aber könnt ihr mir vielleicht trotzdem ein paar anhaltspunkte (oder auch nen guten link) geben, wie man solche design-entscheidungen trifft.

ich habe es bei kleineren nur für mich geschriebenen programmen meist so gemacht, dass ich eine exception dort abfange, wo sie auftritt, dann ne fehlermeldung ausgebe und danach meist einfach null returniere. (sprich ich habe meist keine eigenen exceptions verwendet). ganz konkret hab ich das eigentlich immer dann gemacht, wenn die aufrufende klasse sowieso nur wissen muss, ob alles geklappt hat oder nicht (und nicht den grund wissen muss).
aber in obigem fall ist dies ja nicht so, denn man möchte eventuell durchaus wissen, woran es lag, dass die linie nicht gezeichnet werden konnte. läuft das graph-programm dann zb noch als eigenständiges programm, kriegt man auch eventuell auch keine logausgaben mehr.

also sollte man, wenn man nicht zu faul dafür ist, möglichst viele exceptions thrown, diese aber schon nesten, wo es geht (also zb schon nen OberTyp wie CannotAddPointException machen). oder hängt es auch von der vererbungshierarchie ab: also zb dass es es drauf ankommt, ob es sinn macht, dass die PointExceptions UnterTypen von LineExceptions sind und man je nachdem in der MethodenSignatur nur den OberTyp throwt oder eben Point und Line Exceptions...? oder sollte ich dann sogar eines drüber gehen und ne GraphException machen? ..

also wie ihr sehr, ist mir da vom design her einiges unklar


----------



## Leroy42 (23. Feb 2007)

Also meine Meinung ist, es mit Exceptions nicht zu übertreiben;
als Coder möchte ich auch nicht gerade bei jeder 10. Zeile eine
Exception-Behandlung einbauen müssen.

Exceptions find ich sinnvoll und unersetzlich bei Kommunikation
mit _externen Medien_, also Dateien, Datenbanken, Sockets, ...
bei denen es in der Natur der Sache liegt, daß Probleme auftreten können.

Auch bei Methoden die _parsen_ (parseInt, parseDate, parsen des 
Format-Strings bei printf-Methoden) machen Exceptions natürlich Sinn.

In fast allen anderen Fällen, denke ich, sollte man mit den bereits
gegebenen Exceptions auskommen und _selbst_ semantisch
diszipliniert arbeiten.


----------



## Lim_Dul (23. Feb 2007)

Exceptions sollen Fehler oder fehlgeschlagene Aufrufe anzeigen, die man anders nicht sinnvoll abfangen kann.

Von daher würde ich bei deinem Beispiel folgendes machen:

```
MyPoint(int x, int y) throws IllegalArgumentException
```


```
void addPoint(MyPoint point) // Nichts
    void addPoint(int x, int y) throws IllegalArgumentException
    MyPoint getPoint(MyPoint point) // Nichts, liefert null zurück
    MyPoint getPoint(int x, int y) throws IllegalArgumentException

    void addLine(MyPoint p1, MyPoint p2) throws IllegalArgumentException
    void addLine(int x1, y1, x2, y2) throws IllegalArgumentException
    MyLine getLine(MyPoint p1, MyPoint p2) //
```


Generell gilt: CheckedException wirklich nur dann werfen, wenn der Fehler auch durch saubere Programmierung nicht verhindert werden kann. Sonst nur RuntimeExceptions!

Und die Exception IllegalArgumentException fasst alle Fälle zusammen, dass ein übergebener Parameter außerhalb des spezifierten Gültigkeitsbereich ist.

Genau das ist bei deinen Methoden der Fall. Ansonsten würde ich sinnigerweiße spezifizieren, dass die Methoden getPoint(...) null liefern, wenn der Punkt nicht im Graphen ist. Das ist ein sinnvolles Verhalten und Exceptions wäre hier mit Kanonen auf Spatzen schießen, da dann vorher immer ein Aufruf von if (graph.contains(...) point = graph.getPoint()) sein müsste.


----------



## BarFooß (23. Feb 2007)

```
void addPoint(MyPoint point) // Nichts
    MyPoint getPoint(MyPoint point) // Nichts, liefert null zurück
    void addLine(MyPoint p1, MyPoint p2) throws IllegalArgumentException
    MyLine getLine(MyPoint p1, MyPoint p2) //
```
Bei diesen Methoden dürfte eine NullPointerException sinnvoll sein.


----------



## Lim_Dul (23. Feb 2007)

Stimmt, wobei man sich bei addLine streiten kann, was sinnvoller ist. Keine Ahnung, wie die Konvention da ist.

PS: RuntimeExceptions wie IllegalArgument oder NullPointer muss auch nicht explizit mittels throws im Methodenkopf definieren.


----------



## -frank (23. Feb 2007)

danke mal an euch alle!

der grund warum ich so rumerlege ist folgender: wenn ich meine eigenen kleinen programme habe, spare ich wie gesagt auch sehr mit exceptions. nun ist es aber so, dass das beispielprogramm mit den punkten und linien von anderen programmen genutzt werden wird. auch über remote-kommunikation.
wenn ich das jetzt aus der seite des programmierers betrachte, der damit arbeiten muss, und mir die Java API als beispiel nehme, dann wirft zb die methode boolean remove(Object) des Interface List (http://java.sun.com/javase/6/docs/a...ermeiden gibts ja gerade die exceptions oder?


----------



## KSG9|sebastian (23. Feb 2007)

Es macht nur Sinn eine checked Exception zu werfen wenn der "Empfänger" auch was mit der Exception anfangen kann.
Was soll ich in nem Programm mit einer SQL-Exception anfangen? Die GUI z.b. kann damit nichts machen und vorallem den Fehler auch nicht "repariere".


----------



## Lim_Dul (23. Feb 2007)

Vorsicht, du wirfst da etwas durcheinander.

Die Exceptions, die List.remove wirft, haben nichts damit zu tun, dass das Element nicht gelöscht werden konnte, sondern dass Programmier eine illegale Operation durchführt.

Wenn die Liste typisiert ist, dann ist das verwenden eines nicht passenden Objekts ein Fehler => ClassCastException
Wenn die Liste keine null-Elemente erlaubt, ist der versuch so eins zu entfernen, ein Fehler -> NPE
Wenn die Liste kein remove unterstützt, ist das aufrufen so einer Methoder ein Fehler -> Exception


Exceptions sind nicht dazu da, mehr Infos zu liefern, sondern Fehlverhalten aufzuzeigen! Wenn eine Exception geworfen wird, dann ist der Regelfall, dass der normale Programmfluss nicht mehr sinnvoll ist. Und das ist, wenn man darüber nur Infos liefern will, nicht der Fall. Dann wird der Code deutlich unleserlicher, weil man dauernd try/catch Blöcke hat.


Überflüssige checked Exceptions sind eine Pest und verunstalten den Code tierisch und machen das lesen des Codes schwerer wenn alle 5 Zeilen ein try/catch Block erfolgt. 


> laut sun gilt ja folgende regel: "If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception."


Das beinhaltet meines Erachtens aber auch, dass ein Fehler aufgetreten ist, den man per Exception kundtun *muss*


Wenn du dir mal die checked Exceptions, die definiert sind, anschaust, dann wirst du feststellen, dass bei keiner du durch sauberes Programmieren verhindern kannst, dass diese auftreten. Das heißt, man muss beim programmieren einbeziehen, dass so ein Fehlerfall auftreten kann und entsprechend recovern.
Alle RuntimeExceptions kann man in 99% der Fälle durch sauberes programmieren verhindern, was bedeutet, dass beim auftreten dieser der Programmier nicht sauber gearbeitet hat und demnach es im Programm nicht sinnvoll weitergehen kann.




> würdet ihr dann vielleicht auch ne variante mit verschiedenen error-codes wählen? (so quasi switch(addLine(..)): case SUCCESS.. case XYERROR..)


Nein, da hast du Recht, dafür gibt es Exceptions.

Die Frage ist aber eine andere, ist es ein Fehler, getLine mit zwei Punkte aufzurufen, wenn zu diesen Punkten keine Linie existiert? Da ist das meines Erachtens zuviel Overhead dafür Exceptions zu verwenden, sondern da kann man auch wirklich einfach null zurückliefern.

Zum andern hat deine Klasse bestimmt eine Methode containsLine(...). Daher ist bei sauberer Programmierung garantiert, dass die Linie existiert, was im Fehlerfall eine RuntimeException bedeuten wird.


----------



## BarFooß (23. Feb 2007)

Lim_Dul hat gesagt.:
			
		

> Stimmt, wobei man sich bei addLine streiten kann, was sinnvoller ist. Keine Ahnung, wie die Konvention da ist.


Die Frage NullPointer ./. IllegalArgument stellt sich IMHO gar nicht, da beide unterschiedliche Fälle abdecken.
Eine NPE tritt auf, wenn keine gültige Objekt-, sondern eine Nullreferenz vorliegt. Bei einer IAE hingegen wurde zwar etwas != null übergeben, hat aber aus irgendwelchen Gründen einen ungültigen Wert. Ein int, der als Index in Arrays, Listen etc. verwendet wird, darf z. B. nicht < 0 sein.
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/IllegalArgumentException.html
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/NullPointerException.html

Ich persönlich würde obiges Beispiel folgendermaßen lösen:
	
	
	
	





```
class MyPoint {
    MyPoint(int x, int y) throws IllegalArgumentException; // zb negative koordinaten
        // aber nur, wenn das Koordinatensystem ausschließlich im positiven Bereich ist
    {..}
}

class Graph {
    void addPoint(MyPoint point) // Nichts. Was ist so schlimm, wenn der Punkt bereits existiert?
    void addPoint(int x, int y) throws IllegalArgumentException // siehe MyPoint-Konstruktor
    MyPoint getPoint(MyPoint point) throws NullPointerException
        // Den Sinn einer NoSuchPointException verstehe ich nicht.
        // Ach so, wenn der gewünschte Punkt nicht im Graphen eingetragen ist.
        // [b]return null[/b] halte ich eher für eine schlechte Idee
        // Wenn in diesem Fall eine Exception geworfen wird, mach' auf jeden Fall auch eine Methode
    boolean containsPoint(MyPoint point)  throws NullPointerException
        // damit das auch ohne Exceptionhandling überprüfbar ist
    MyPoint getPoint(int x, int y) throws IllegalArgumentException // NoSuchPointException: s. o.
        // Ich frage mich gerade, ob getPoint() nicht überflüssig ist. Der Punkt an sich ist ja bekannt.
        // containsPoint() würde bereits ausreichen.
    void addLine(MyPoint p1, MyPoint p2) throws NullPointerException // Aber auch angeben, welcher Parameter.
    void addLine(int x1, int y1, int x2, int y2) throws IllegalArgumentException
    MyLine getLine(MyPoint p1, MyPoint p2) throws NullPointerException // s. o.
        // Dies soll vermutlich die Linie / Strecke zwischen den beiden Punkten zurückgeben.
        // Wenn beide Punkte gleiche Koordinaten haben, würde ich weder null liefen noch Exceptions werfen,
        // sondern ein gültiges MyLine-Objekt. Die Strecke zwischen den Punkten beträgt dann halt 0.
}
```


----------



## -frank (23. Feb 2007)

100% sicher wird es mit sauberer programmierung (des clients) wohl auch nicht werden, da ich wie gesagt davon ausgehe, dass durch andere programme/threads der graph verändert werden kann. um eine contains() abfrage und danach ne operation 100% sicher zu machen, müsste ich die zwei aufrufe atomar machen sprich mir einen lock auf den graphen holen. (was ich aus jetziger sicht nicht möchte). weiters will ich nicht zuviele unnötige remote-aufrufe von contains().

andererseits versteh ich deine/eure argumente schon. hmm...

wie wärs damit:


```
line = graph.getLine(1,1,2,2); // wenn hier ne runtime exception geworfen wird, sind meine koordinaten illegal, also ein fehler im client. das muss ich nicht catchen...
if(line == null) {
    System.err.println("Konnte auf Linie (1,1,2,2) nicht zugreifen! Nähere Info:");
    if(!graph.containsLine(1,1,2,2) {
        System.err.println("Linie existiert nicht!");
        if(!graph.containsPoint(1,1) {
            System.err.println("P1 existiert auch nicht");
        }    
        if(!graph.containsPoint(2,2) {
            System.err.println("P2 existiert auch nicht");
        }    
    
    } else {
        System.err.println("fehler ist mir ein rätsel ;) ");
    }
    return;
}
// tue etwas mit linie
..
```

findet ihr diese variante ok?


----------



## KSG9|sebastian (23. Feb 2007)

Was willst du mit System.err bezwecken?
Du solltest (wenn überhaupt) die logging-Ausgaben in dem entsprechenden Bereich der "Geschäftslogik" vornehmen.


----------



## -frank (23. Feb 2007)

KSG9|sebastian hat gesagt.:
			
		

> Was willst du mit System.err bezwecken?
> Du solltest (wenn überhaupt) die logging-Ausgaben in dem entsprechenden Bereich der "Geschäftslogik" vornehmen.



verstehe nicht, was du meinst. der code soll ein beispiel-code auf client-seite sein. die "geschäftslogik" dort ist es eben, punkte und linien zu adden/removen. wenn da was nicht klappt, will ich an der stelle auch ne ne logausgabe. (bzw. statt system.err... will ich eben ne message auf der gui ausgeben lassen oder sowas).

@barfooß:
das mit den Points funktioniert zb so, dass zwei punkte als gleich gelten, wenn sie die gleichen koordinaten haben. deswegen macht die methode MyPoint getPoint(MyPoint) sinn. denn ich bekomme so das entsprechende MyPoint objekt mit denselben koordinaten. die Punkte haben aber zusätztliche eigenschaften außer x,y und sind keine normalen Java-API Point Objekte (das wollte ich mit dem Namen MyPoint signalisieren. hätte ich vielleicht erklären sollen). ein MyPoint hat also zb noch ne farbe. standard bei new MyPoint(x,y) ist schwarz, aber man kann sie auch auf rot setzen. nun macht es natürlich nen unterschied, ob der nach addPoint(myPoint) ein roter oder ein schwarzer punkt am graphen ist.
ich könnte sowas natürlich immer vorher mit contains() absichern und nachher mit getPoint() überprüfen, aber es wäre mir halt lieber gewesen, wenn ich das ganze über die returnvalue bzw. exceptions mitbekomme. ein fehler ist wie gesagt kein problem, das client programm muss nur darüber informiert werden.

ich würde nun sagen, dass die void methoden einfach boolean methoden werden sollten (das wird OK bzw. ERROR signalisieren) und bei den nicht-void methoden gilt eben null als fehler/nicht gefunden. für zusätzliche infos gibts dann die contains/get methoden. denke das müsste so klappen. die vielen exceptions brauche ich dann nicht, nur die runtime-ex bei echten fehlern der clients.


----------



## BarFooß (24. Feb 2007)

-frank hat gesagt.:
			
		

> das mit den Points funktioniert zb so, dass zwei punkte als gleich gelten, wenn sie die gleichen koordinaten haben. deswegen macht die methode MyPoint getPoint(MyPoint) sinn. denn ich bekomme so das entsprechende MyPoint objekt mit denselben koordinaten. die Punkte haben aber zusätztliche eigenschaften außer x,y und sind keine normalen Java-API Point Objekte


Dass MyPoint nicht aus der Standard-API ist, war mir klar und durch das Vorhandensein weiterer Unterscheidungsmerkmale ändert sich die Sache. Dann stellt sich nämlich zusätzlich die Frage was passiert, wenn mehrere Punkte gefunden werden - z. B. ein roter, ein gelber und ein weißer. Ich würde die jetzigen getPoint()-Methoden streichen und durch
	
	
	
	





```
MyPoint[] getPoints(MyPoint)
```
ersetzen. Sind an der angegebenen Stelle keine Punkte, wird ein *Array der Länge 0* zurückgeliefert. Dadurch dürfte die NoSuchPointException überflüssig geworden sein. NPE & IAE bleiben wie oben beschrieben.

Sofern ich das Koordinatensystem richtig verstehe (ausschließlich positive Koordinaten), gibt es Integer.MAX_VALUE * Integer.MAX_VALUE Möglichkeiten, darin Punkte zu verstecken. Statistisch gesehen ist es deshalb vermutlich ein Ausnahmefall, wenn ausgerechnet da, wo du suchst, auch tatsächlich etwas gefunden wird.


----------



## -frank (25. Feb 2007)

BarFooß hat gesagt.:
			
		

> Dass MyPoint nicht aus der Standard-API ist, war mir klar und durch das Vorhandensein weiterer Unterscheidungsmerkmale ändert sich die Sache. Dann stellt sich nämlich zusätzlich die Frage was passiert, wenn mehrere Punkte gefunden werden - z. B. ein roter, ein gelber und ein weißer.



naja, ich wollte es schon so definieren, dass an einer stelle x,y nur ein punkt sitzen kann.


----------

