Hallo Martin,
Im Gegenteil. Wo immer ich die Möglichkeit sehe, ein Häufchen thematisch abgeschlossenen Code in eine Funktion auszulagern, dann tu ich das - außer wenn ich dieses Stück Programmcode dann wieder nur einmal nutze. In diesem Fall lasse ich's lieber inline.
Ich meinte nicht, dass Du Dein Programm nicht strukturierst. Mit "Funktionalem Programmierstil" meinte ich etwas, wie es im Extremfall in funktionalen Sprachen auftritt. Man drückt da ja letzten Endes alles aus, indem man Funktionen zusammensetzt. In imperativen Sprachen kann das natürlich ineffizient sein, weshalb man es da nicht immer durchziehen kann. Außerdem unterstützt die Syntax das je nach Sprache natürlich nicht so.
Die eingestreuten try...catch-Blöcke empfindest du nicht als Verschmutzung?
Naja ich muss sie ja meistens eben nicht einstreuen, oft reicht ein Block für eine ganze Funktion (evtl. mit 2-3 catch-Blöcken). Nur in Programmteilen, wo Fehler sehr oft kompensiert werden können, kommen vielleicht mal mehrere try-catch-Blöcke vor, da gibt es aber eigentlich keinen Unterschied zum Prüfen des Rückgabewertes außer der Sytax.
Zwischen error = foo(); if (error) {...; return;} und try{foo()} catch(Exception e) {...}; ist ja nun wirklich kein Unterschied.
Ja, und die Fehlerbehandlung findet (im Programmcode) woanders statt als der reguläre Ablauf. Deswegen sagte ich, ich finde das Konzept unübersichtlich und manchmal schwer zu verfolgen.
Hm ok, wenn man es schlecht macht. Wenn man es gut macht, kann man den normalen Ablauf schön erkennen und dann noch nachsehen, was bei einem Fehler passiert.
Ja, eben:
~~~
function DoThis(blah, blubb, blaeh)
{ DoSomethingWith(blaeh);
return (DoThat(blah+1, blubb));
}
>
Ja, weil DoThat die gleichen Fehlercodes hat, wie DoThis und weil das Ergebnis direkt zurückgegeben wird. Aber aus folgendem Code:
function DoThis(a, b, c) {
return 5 \* DoThat(a + DoThat2(b, c), 3);
}
Wobei DoThat und DoThat2 aus verschiedenen Modulen/Biblotheken kommen und jeweils einen unterschiedlichen Fehler mit -1 signalisieren wird z.B:
function DoThis(a, b, c) {
int result = DoThat2(b, c);
if (result == -1) {
return -1;
}
result = DoThat(a + result, 3);
if (result == -1) {
return -2;
}
return 5 \* result;
}
Hier findet jetzt auch noch die besagte Vermischung von Fehlercode und Daten statt. Das könnte man natürlich über Referenzen/out-Parameter o.ä. umgehen, bei Dir sähe das also wohl so aus:
function DoThis(a, b, c) {
int result;
if (DoThat2(b, c, out result) == -1) {
return -1;
}
if (DoThat(a + result, 3, out DoThat2(b, c, out result) == -1) {
return -2;
}
return 5 \* result;
}
Mit Exceptions bräuchte man überhaupt keine zustätzlichen Code, will man die Exceptions durch andere ersetzen, was manchmal sinnvoll ist und in etwa dieser Umwandlung von Fehlercodes entspricht, tut man sowas:
function DoThis(a, b, c) {
try {
return 5 \* DoThat(a + DoThat2(b, c), 3);
} catch (Exception1 e) {
throw new MyException(e);
} catch (Exception2 e) {
throw new MyException(e);
}
}
Das ist wesentlich kompakter, außerdem ist es egal, ob man DoThat oder DoThat2 je einmal oder zehnmal verwendet.
> ~~~
if (error=FunctionThatMightFail())
> { // z.B. Aufruf einer zentralen Fehlerbehandlung
> // oder Abfrage mit switch (error)
> }
> // weiter im normalen Programmfluss
Ja, genau diese Konstrukte sind die, die den Code kompliziert machen. Wenn man sie tatsächlich braucht, sind Exceptions ja wie gesagt gleichwertig verwendbar.
Dann ist das Gesamtkonzept schlecht durchdacht. Wenn ich Module schreibe, die die Gesamtheit der Funktionalität zu einem bestimmten Thema bündeln, dann verende ich auch im ganzen Modul einheitliche Bezeichner, Fehlercodes und Aufrufkonventionen.
Ja für ein Modul, sobald man mehrere, fremde Module verwendet, klappt das leider nicht mehr. Exceptions erzwingen da auch eine Art Minimalkonvention, was auch schon etwas Wert ist.
Das sollte man natürlich vermeiden - oder man muss *ganz genau* wissen, was man tut.
Menschen machen Fehler, entweder schlicht aus Unwissen, aufgrund schlechter Dokumentation eines Moduls oder eben einfach, weil es jedem manchmal passiert. Entscheidend ist also, was im Fehlerfall passiert. Eine Exception führt jedenfalls dazu, dass man den Ort mit falscher/fehlender Fehlerbehandlung gut lokalisieren kann. Man kann sogar teilweise Typprüfung dafür verwenden, wobei ich jetzt nicht auch noch deren Vor- und Nachteile diskutieren möchte ;-)
Was machst Du, wenn Du eine Datei in einem Programm lesen musst, aber das nicht kannst? Musst Du da nicht aus dem regulären Kontrollfluss ausbrechen?
So besser? ;-)
Grüße
Daniel