Codenavigation durch Exceptions?

takidoso

Erfahrenes Mitglied
cosmochaosmaker hat gesagt.:


Erstmal vielen Dank für Deinen Link, er war Programmierphilosophisch sehr interessant obgleich ich nicht alle Punkte darin teilen kann.
Der wesetnlichste Punkt den ich zumindes aus meinen Erfahrungen heraus festhalten kann ist die Versionierung, die mitunter an diesen Stellen tatsächlich kompliziert werden könnte.
Auf jedenfall ist deutlich zu merken, dass Java-phylosophie genau in dem Punkt Exception-Deklaration und Überwachung durch Compiler von C#-phylosophie abweicht.

nun aber zu Deinem Satz den ich noch etwas mehr präzisiert haben möchte. Auf welche der drei Beispiele war die von Dir angemerkte Unsauberkeit bezogen?

Das mit dem Einschließen von try und catch war lediglich (Beispiel 1 und 2) eine Demonstration wie man prinzipiell das ExceptionHandling, so man es tatsächlch verwenden möchte (nehmen wir mal an es gäbe nur unchecked-Exceptions unabhängig von der Sprache) weiter zentralisieren könnte/kann.

das 3. Beispiel war lediglich die Darstellung wie es rein prozedural ohne Exceptions aussehen könnte. Also klassisches abfangen von Returncodes, die Erfolgsmeldungen darstellen.
Typisches Beispiel wären z.B. Datenbankabfragen. Entweder bietet die Programmierumgebung Exceptions an in denen DB-Fehler gemeldet werden, oder es geschieht przeural nur über returncodes, die ältere Form wie es vor Objektorientierung stattfand.

welche der 3 Varianten findest du aus Deiner Sicht am schönsten?

Takidoso
 

Christian Kusmanow

Erfahrenes Mitglied
Ich find die erste Variante am schönsten. :)
Sie übernimmt doch die Behandlung deiner zweite Variante intern. Also OOP gerecht.
Alle meine Klassen haben ihren eigenen ExceptionHandler. Bsp.

Die dritte Variante ist zu aufwendig, weil vorher alles akribisch ausklamüsert werden muss.
Ausserdem nicht OOP gerecht weil der Erfolgreiche Abschluss der Aktion,
ausserhalb ausgewertet wird. Das verwerten des Ergebnisses wird also unübersichtlich.
Ein true, false, null oder der Wert, sollten es sein.
Was ist vor allem wenn Du vor dem auswerten des return Codes doch einen Überlauf hast?
Ein robustes Programm darf bei einer Ausnahme nicht abschmieren! Wie sieht denn das aus?
Wie soll man als Enwickler von dem Fehler erfahren wenn man seinem Proggi nicht die Chance gibt,
zu melden was schief gehangen ist?
Man sollte immer ein Art Notbremse bereistellen finde ich.

Application.Run() gehört zB auch in einen try-catch Block.
Das ganze in einer Schleife und man könnte es sogar gleich wieder neu starten,
sofern dort eine checked Exception ausgelöst wurde.

MfG, cosmo
 

takidoso

Erfahrenes Mitglied
Ja da bin ich mit Dir in allen Punkten voll einer Meinung!
die dritte Variante kommt aus der Zeit der prozeduralen Programmierung und bläht den eigetnlichen fachlichen Code gnadenlose auf. In der Firma, in der ich vor mittlerweile über 5 Jahren arbeitete, hatten wir keinerlei OO-Entwicklungsumbegung. Um nun ein Fehlen von Returncode-Abfragen zu erkennen gab es vorgeschaltete Preprozessoren, die ganz automatisch ein Programm, welches nicht alle Codes abfragte das Eingehen in die Produktion verhinderten. Also wenn man so will wurde damit der Entwickler kontrolliert. Ähnlich verhält es sich mit checked-Exceptions: Philosophisch gesehen soll der Entwickler schon zur Compilezeit darauf aufmerksam gemacht werden, dass er vermutlich was vergessen hat.

Takidoso
 

takidoso

Erfahrenes Mitglied
cosmochaosmaker hat gesagt.:
Die dritte Variante ist zu aufwendig, weil vorher alles akribisch ausklamüsert werden muss.
Ausserdem nicht OOP gerecht weil der Erfolgreiche Abschluss der Aktion,
ausserhalb ausgewertet wird. Das verwerten des Ergebnisses wird also unübersichtlich.
Ein true, false, null oder der Wert, sollten es sein.
Es ist oft so, dass ein anderes System (z.B. eine DB) Fehlercodes mitgibt, um genauere Information darüber zugeben was falsch lief, somit ist true, false oder null in solchen fällen nicht zu erwarten. Aber wie gesagt, das waren rein prozedurale Techniken etwas auf alle evantualitäten funktionsfähig zu haben, mal von einem Stack-overflow abgesehen.

Takidoso
 

Christian Kusmanow

Erfahrenes Mitglied
Ersmal fühle ich mich geehrt mit so einem alten Hasen wie Dir einer Meinung zu sein. :)

@Fehlercodes:
Wieso sollte die Funktion Angaben zum Fehler machen?
Stell Dir vor Du arbeitest mit einem DAL. Und es gibt eine DB-Funktion die einen Datentyp(Datensatz) zurück gibt.
Wie soll der FehlerCode zurückgegeben werden? Den Ruckgabetyp auf object ändern
und entweder den Datentyp oder den Fehlercode zurückgeben?
Dann müsste zusätzlich vor dem zuweisen noch das object überprüft werden.
Das würde doch den Code wieder unübersichtlich machen.
Sollte nicht die Klasse Flags bereitstellen um dies woanders genauer auszuwerten?

Datenverwertung und Fehlerbehandlung währen doch so sauberer getrennt.
Was sagst Du?

lg, cosmo
 

takidoso

Erfahrenes Mitglied
cosmochaosmaker hat gesagt.:
@Fehlercodes:
Wieso sollte die Funktion Angaben zum Fehler machen?
Stell Dir vor Du arbeitest mit einem DAL. Und es gibt eine DB-Funktion die einen Datentyp(Datensatz) zurück gibt.
Wie soll der FehlerCode zurückgegeben werden? Den Ruckgabetyp auf object ändern
und entweder den Datentyp oder den Fehlercode zurückgeben?
Dann müsste zusätzlich vor dem zuweisen noch das object überprüft werden.
Leider ist mir die Abkürzung DAL nicht bekannt. Anderseits darfst Du Dir die prozedurale Welt nicht zu objektorientiert vorstellen. Natürlich ist mein 3. Beispiel etwas verkürzt dargestellt. Du hast wenn ich mich recht entsinne in einem Embeded-SQL gewisse Macros zur Verfügung gestellt bekommen die Neben dem SQL Fehler-code und einem weiteren redundanten aber DB-Systemunabhängigen (weil normierter) Return-Code no dies und das bekommen. Bei Erfolgsmeldung hat man dann einfach die Datensätze iteriert. Macht man heute eigetnlich genauso. Der generelle Unterschied zu früher ist halt nur, dass eine Exception die weiterverarbeitung bis zum ende des TRY-blocks fluchtartig verlässt (also eigentlich ein etwas der Strukturierten Programmierung widerstrebendes aber recht nützliches Verhalten, wenn man dieses Idiom kennt)

cosmochaosmaker hat gesagt.:
Das würde doch den Code wieder unübersichtlich machen.
Sollte nicht die Klasse Flags bereitstellen um dies woanders genauer auszuwerten?
Wie gesagt Klassen kennt die Prozedurale Welt eigentlich nicht.
cosmochaosmaker hat gesagt.:
Datenverwertung und Fehlerbehandlung währen doch so sauberer getrennt.
Was sagst Du?
lg, cosmo
Da hast du absolut recht. Jedoch sollte man eine gewisse Historie sehen bzw. verschiedene Programmierphilosophien/Praktiken sehen.

Nehmen wir doch mal das gute alte Pascal. Damit meine ich nicht die späteren Pascaldialekte wie z.B. Turbopascal, sondern die älteren Formen der Sprache.
Es gab dort Prozeduren (ohne Rückgabewerte aber Möglichkeit von ein und Ausgabeparmertern) und Funktionen, die normalerweise dafür verwendet wurden Berechnungsergebnisse zurückzugeben.
Im Vergleich dazu C dort gab es nur noch den Begiff der Funktionen, wobei das im wesentlichen sich nur um ein terminologischen Unterschied handelt. Aufjedenfall war es in C natürlich auch in den Standardbibliotheken üblich Returncodes als Erfolgs oder misserfolgsmeldungen zurück zugeben andererseits auch nicht selten als Wert. Also eine recht gemsichte angelegenheit ähnliches finden man auch noch in heutigen Standardbibliotheken, z.B. in Funtionen für die Stringanalyse.(RC= -1: zu suchendes Zeichen nicht gefunden und RC>=0 : Index wo zu suchendes Zeichen enthalten ist).
Also eine typsiche Vermischung von Erfolgsmeldung und Ergebniswert.
Es scheint halt praktikabler an der Stelle so zu verfahren, allerdings gilt es eigetnlich für sauberer Erfolgbewertung und weiterzuverarbeitender Wert zu trennen.

Also es werden nicht selten auch in hoch offiziellen Bibliotheken in manchen Fällen solches Vermischt ich vermute mal es ist ein Stückchen C-Pragmatismus.

Takidoso
 

Christian Kusmanow

Erfahrenes Mitglied
Meine Güte,

dein Wissen macht mir Angst. :D
Du scheinst ein einer dieser "älteren" Cracks von damals zu sein. :)
takidoso hat gesagt.:
Leider ist mir die Abkürzung DAL nicht bekannt. Anderseits darfst Du Dir die prozedurale Welt nicht zu objektorientiert vorstellen. Natürlich ist mein 3. Beispiel etwas verkürzt dargestellt. Du hast wenn ich mich recht entsinne in einem Embeded-SQL gewisse Macros zur Verfügung gestellt bekommen die Neben dem SQL Fehler-code und einem weiteren redundanten aber DB-Systemunabhängigen (weil normierter) Return-Code no dies und das bekommen. Bei Erfolgsmeldung hat man dann einfach die Datensätze iteriert. Macht man heute eigentlich genauso.
[thread=191381]Was zu Thema DAL (Datenbank-Abstraktions-Layer)[/thread]
Wie soll ich das verstehen? Vermeidest Du jetzt prinzipiell das Ergebnis zurückzugeben
und verwendest anstelle ReturnCodes und entscheidest dann den Wert auszulesen,
den Du derweil woanders hin gespeichert hast?
Ist das nicht ein bissel "pessimistisch programmiert"?. :confused:
Ich will Dich wirklich nicht kritisieren, nur verstehe ich den Sinn Vorgehensweise nicht ganz.
Vor allem wenn man von der Funktion einen Typ anstatt irgend einen Zahlenwert erwartet.

Nicht das Du mich falsch verstehst, ich meinte nicht generell.

Es gibt natürlich auch andere Scenarios wo vor dem Verwerten des Ergebisses,
der Erfolgsstatus gebraucht wird.
Quasi wenn Du Du dir sowas wie ein Objekt baust welches mit Read() usw arbeitet.
(wie beim IO.Stream) Dann ist es logischerweise praktikablerer mit einer Funktion,
die Daten zu lesen zu prüfen ob und wieviel gelesen werden konnte.
Oder wie Du schon sagtest best. StringFunktionen.

Andernfalls bin ich der Meinung das man es optimistischer lösen kann
und geilchzeitig einen klareren Ablauf sieht.
Ich finde es ist dann viel übersichtlicher was mit den Daten passiert,
weil sie gleich verwertet werden.
Ich prüfe zB auf != null und kann andernfalls einfach abbrechen (1 Zeile)
und werte dann den Fehler im "ErrorHandler" meiner Klasse mit hilfe der "Flags" aus.
Dann brauch ich den ReturnCode in der Funktion gar nicht zu überprüfen.
Ich mag das einfach nicht in den Methoden/Funtionen haben die mit den Ergebnissen arbeiten.
Das bläht sich dann alles so auf und wird für mich unübersichtlich.

takidoso hat gesagt.:
Der generelle Unterschied zu früher ist halt nur, dass eine Exception die weiterverarbeitung bis zum ende des TRY-blocks fluchtartig verlässt (also eigentlich ein etwas der Strukturierten Programmierung widerstrebendes aber recht nützliches Verhalten, wenn man dieses Idiom kennt)
Sollte aber auch nur eine Notbremse sein find ich.
Exceptions als Programmflusssteuerung zu verwenden ist Schwachsinn.
Ausser man will von aussen einen Thread abbrechen
oder seine Propertys richtig wertzuweisungssicher machen usw. .

takidoso hat gesagt.:
Also es werden nicht selten auch in hoch offiziellen Bibliotheken in manchen Fällen solches Vermischt ich vermute mal es ist ein Stückchen C-Pragmatismus.
Ich denke es ist nur ein Ding der Notwendigkeit. :p
Also Scenarios, bei denen der Erfolgsstatus bekannt sein muss,
bevor das Ergbnis verwertet wird.

lg, cosmo
 

takidoso

Erfahrenes Mitglied
cosmochaosmaker hat gesagt.:
Meine Güte,

dein Wissen macht mir Angst. :D
Du scheinst ein einer dieser "älteren" Cracks von damals zu sein. :)
Keine Sorge, es gibt verschiedene Formen des Wissens. Ich habe als 15 Jähriger die ersten Programme geschrieben, wie vermutliche viele andere im ähnlichen Alter ebenso.
Es war halt in der frühen Mitte der 80'er, in der, so man interessiert war, mit Homecomputern nicht nur spielend sondern auch programmiertechnisch auseinandersetze. Damals hatte ich vor allem Neugier und teilweise Gelegenheit mich mit einigen verschiedenen Programmiersprachen zu beschäftigen, wobei es weniger in große Tiefen ging sondern mehr um die Philosophien der jeweiligen Sprachen. Insofern habe ich, selbst wenn ich mal so etwas wie Spezialwissen gehabt haben sollte, mich mehr für das prinzipielle interessiert.

cosmochaosmaker hat gesagt.:
Vermeidest Du jetzt prinzipiell das Ergebnis zurückzugeben
und verwendest anstelle ReturnCodes und entscheidest dann den Wert auszulesen,
den Du derweil woanders hin gespeichert hast?
Ich bin mir nicht sicher, ob du da komplizierter denkst als es eigetnlich ist. Wenn Dir z.B. ein zugriff auf eine DB-Tabelle nicht geklappt hat, weil keine Connection, Statement falsch oder Constraint, der sich beschwert etc, ist es sinnlos irgendwelche Daten einlesen zu wollen. In heutigen Umgebungen bekommst du eine Exception und der fachliche code muss sich nicht damit rumschlagen und sieht halt auch fachlich aus. In einer Umgebung ohne Exception (so wie es halt früher war) hast Du mit einem if then else oder case-anweisung (in c ähnlichen Sprachen switch-anweisung) halt darauf reagiert, das ist eigentlich alles. Also so wie im 3. Beipiel.
cosmochaosmaker hat gesagt.:
Ist das nicht ein bissel "pessimistisch programmiert"?.
...
cosmochaosmaker hat gesagt.:
Vor allem wenn man von der Funktion einen Typ anstatt irgend einen Zahlenwert erwartet.

Ja es ist pessimistisch programmiert, man tut es ja nicht mit jeder beliebingen Funktion, sondern nur dann wenn etwas nicht klappen könnte, sei es aus Technischen oder aus Programmeirfehlergründen.
Normalerweise ist ja sowas nur dann der Fall wenn man mit anderen Systemen kommuniziert, typisches Beispiel DB oder Netzwerk.
Der prozedurale Ansatz wird in heutigen modernen Systemen natürlich nicht mehr gemacht, dafür gibt es halt mittlerweile Exception-Handling.

cosmochaosmaker hat gesagt.:
Es gibt natürlich auch andere Scenarios wo vor dem Verwerten des Ergebisses,
der Erfolgsstatus gebraucht wird.
Quasi wenn Du Du dir sowas wie ein Objekt baust welches mit Read() usw arbeitet.
(wie beim IO.Stream) Dann ist es logischerweise praktikablerer mit einer Funktion,
die Daten zu lesen zu prüfen ob und wieviel gelesen werden konnte.
Oder wie Du schon sagtest best. StringFunktionen.
StringFunktionen sind ein typisches Beispiel wo das ganze ohne Exceptions gelöst wird, weil solche Fehler wirklich zu oft passieren können und vermutlich ja gar keine Exception-würdige Situation ist.
cosmochaosmaker hat gesagt.:
Das bläht sich dann alles so auf und wird für mich unübersichtlich.
Genau das ist ja auch der Grund, warum man ungewöhnliche technische Probleme eben nicht mehr mit Fallunterscheidungen beglückt.
cosmochaosmaker hat gesagt.:
Sollte aber auch nur eine Notbremse sein find ich.
Exceptions als Programmflusssteuerung zu verwenden ist Schwachsinn.
Wir beide sind da absolut konform, man sollte Exceptions nur als solche verwenden was sie nunmal auch sind, nähmlich Ausnahmen!
cosmochaosmaker hat gesagt.:
Ich denke es ist nur ein Ding der Notwendigkeit. :p
Also Scenarios, bei denen der Erfolgsstatus bekannt sein muss,
bevor das Ergbnis verwertet wird.
Richtig, entweder man benötigt ein Ergebnis und es wäre falsch keines zu bekommen oder ein Ergebnis nicht zu bekommen hat innerhalb der Anwendung einen Semantischen Sinn.

Wo es allerdings Sinnvoll sein kann in seiner eigenen Anwedung Exceptions zu werfen ist z.B. wenn sichergestellt werden soll, dass die einer Mehtode dargebrachten Argumente koreckt sind oder nicht. Ein typisches Beispiel welches ich da rausgreifen mag wären z.B. sogenannte Funktionscodes.
Nehmen wir mal an eine routine erwartet als Argument unteranderem für die nähere Verarbeitungsspezifizierung einen sogenannten Funktionscode aus einer fest definierten Menge, dann kann es sinnvoll sein diesen code auf gültigkeit abzutesten einfachste Möglichkeit wäre das in einem switch/case - Konstrukt
Code:
// erlaubte funcCodes (TUH_DIES, TUH_DAS)
func1 (int funcCode, Object arg1, Object arg2)
{
    switch (funcCode)
    {
        case TUH_DIES: 
              ....
        break;
        case TUH_DAS: 
              ....
        break;
        default: // in der Hoffnung, dass es nie zu dieser Exception kommt
            throw new IllegalArgumentException("falscher Code"+funcCode);  
    }
}
Mal abgesehen, dass IllegalArgumentException vermutlich in C# etwas anders heißen mag, ist der Sinn dieses Konstruktes der, dass man sicher ist, dass die Routine etwas tut oder aber sich beschwert. Damit wird sichergestellt, dass der Programmierer der die Routine aufruft dies auch zumindest in diesem Beispiel mit einem erlaubten Funktionscode tut. Trifft man also eine solche Exception in der Ausführung an, darf man sicher sein, das etwas zu korrigieren ist. Insofern ist aus meiner sicht es nicht notwendigerweise falsch in seinem Programm auch selbst Exceptions zu werfen, mit unter ersparrt das gerade in solchen Szenarios unnötige Analyse Zeit.
Aber wie Du schon bemerkt hast und auch ich immer wieder nur betonen kann, sind Exceptions als Ausnahmezustand anzusehen.

Takidoso
 

Christian Kusmanow

Erfahrenes Mitglied
Vielen dank für deine Antwort. :)
Ich bin mir nicht sicher, ob du da komplizierter denkst als es eigetnlich ist. Wenn Dir z.B. ein zugriff auf eine DB-Tabelle nicht geklappt hat, weil keine Connection, Statement falsch oder Constraint, der sich beschwert etc, ist es sinnlos irgendwelche Daten einlesen zu wollen. In heutigen Umgebungen bekommst du eine Exception und der fachliche code muss sich nicht damit rumschlagen und sieht halt auch fachlich aus. In einer Umgebung ohne Exception (so wie es halt früher war) hast Du mit einem if then else oder case-anweisung (in c ähnlichen Sprachen switch-anweisung) halt darauf reagiert, das ist eigentlich alles. Also so wie im 3. Beipiel.
Ich verstehe was Du meinst.
Nur verwerte ich die Exception schon in der Klasse die die DB liest.
Sie hat ja nach meinem Gutdünken mit dem Rest nichts zu tun.
Wie gesagt, ich möcht das meine Objekte dies selber machen.
Der Rest wird via Flags geregelt.
Eine Flag kann ja auch einen ErrorString aus deinen Ressources repräsentieren.

Dein letztes Bspiel beschreibt auch genau die Situation, die ich mir auch unter einer Exception vorstelle.
Kommt dem nahe seine eine Propertys richtig wertzuweisungssicher machen.
Gutes Beispiel.
In disem Beispiel sieht man zB auch ganz gut, wie man Threads mit Exceptions einfach stoppen kann.
Danke für dein Statement. Das ist das bei weitem Beste, was ich bisher zu dem Thema gelesen hab.
Es bestätigt meine bisherigen Erfahrungen auf dem Gebiet, die ich mit selber angeeignet habe.
Und ich hab dadurch wieder eine bessere Sichtweise, für die Dinge ansich bekommen.

lg, cosmo
 

Norbert Eder

Erfahrenes Mitglied
So, jetzt muss ich mich auch einmsichen :)

Deine Klassen können das nicht immer selber lösen. Entwickelst du zum Beispiel ein Framework inkl. der notwendigen Storage-Klassen etc. dann kann zb die Storage-Klasse Fehler bis zu einem bestimmten Punkt abfangen und selbst behandeln. Ausnahmen, sollten an dieser Stelle weitergereicht werden. Warum? Nun, es sollte der User auch wissen, dass eine Ausnahme aufgetreten ist.

Natürlich müssen hier auch unterschiedliche Dinge bedacht werden. Handelt es sich um eine 3-Tier, 2-Tier oder überhaupt nur um eine "Desktop-Anwendung". Bei einer 3-Tier-Anwendung macht es wenig Sinn Exceptions ganz zurück weiter zu reichen. Hier muss die Stelle definiert werden, an der es Sinn macht die Exception zu behandeln, um die Meldung dann entweder an den Logger zu übergeben, oder ein eventuellen Workflow auszulösen.

Und sicherlich machen eigene Exceptions Sinn. Genau an den Stellen, an denen Ausnahmen auftreten können. Logische Fehler zb. sollten auf keinen Fall durch Exceptions behandelt werden. Aber das sollte ansich auch klar sein.