Artikelformat

Exception werfen oder false zurückgeben

PHP bietet wie viele andere Programmiersprachen die Möglichkeit Exceptions zu nutzen. Viele PHP Funktionen nutzen diese Möglichkeit nicht und setzen auf false. Welche Methode ist besser, gibt es überhaupt eine bessere Methode?

False wird vor allem von Funktionen wie substr, strpos oder ähnliche zurückgegeben wenn ein Fehler aufgetreten ist und im Fall von strpos auch, wenn die „Needle“ nicht gefunden wird. PDO im Gegensatz benutzt Exceptions.

Ein direkter Vorteil von Exceptions ist die Tatsache, das man mehr Fehlerfälle ausdrücken kann, als durch die Rückgabe von false. Man definiert für jeden Fehlerfall eine entsprechende und auch sprechende Exception, die dann geworfen wird. Abhängig vom Fehlerfall kann der umgebende Code entsprechend reagieren. Auch kann man Exceptions in einer höheren Ebene der Aufrufhierarchie behandeln.
Nachteil ist, dass man insbesondere bei PHP nicht direkt weiß, welche Exceptions auftreten können. Auch implizieren Exceptions einige Muster, an die sich ein Entwickler halten sollte. Zum Beispiel sollte nicht die Exception Klasse allgemein gefangen werden, da man nicht alle Fehler, sondern nur spezielle behandeln sollte. In einer index.php ist das wieder ein anderes Thema, denn hier sollte man keine Exception an den Benutzer rausgeben. Weiter sollten Exceptions möglichst lokal behandelt werden. Also bedeutet das, keine zu erwartenden Fehler durch die ganze Aufrufhierarchie wandern lassen. Auf der anderen Seite kann man aber auch Unittests schreiben die mE übersichtlicher sind, da man eine erwartete Exception per Annotation angeben kann. Somit sieht man auf einen Blick, was bei einem fehlerhaften Eingabewert oder Zustand passieren soll.

False hingegen ist in der Benutzung unproblematischer. Man gibt einfach statt einem üblichen Rückgabewert false zurück und der Fehlerfall ist gelöst. Hier muss man beachten, dass keine differenzierte Fehlerdarstellung aus der Funktion heraus möglich ist. Man muss im aufrufenden Code diesen Fehlerfall bedenken und vielleicht analysieren, wenn man mehr als nur „ein Fehler ist aufgetreten“ ausgeben will. Somit eignet sich false eben genau für Funktionen, die genau einen Fehlerklasse kennen und keine Differenzierung benötigen.
Nachteil dieses Vorgehens ist die Behandlung. Man muss bedenken, dass man den Rückgabewert immer auf Nicht-Äquivalenz zu false überprüft. Und jeder PHP-Entwickler kennt Aufrufe wie:

1
2
3
4
5
6
7
$a = strpos("einlangerstring","ge");
if ($a !== false) {
    // tue etwas
}
else {
    // Fehlerfall
}

Und an diesen Stellen treten dann auch gerne Fehler auf, denn false wird dann gerne als 0 interpretiert und schon läuft der Algorithmus Amok oder liefert ein falsches Ergebnis. Wenn man diesen Stolperstein aber beachtet, kann man natürlich auch mit false arbeiten.

Fazit:
Ich würde – wenn möglich – Exceptions nutzen. Diese sind in der Handhabung vielleicht etwas aufwändiger, bieten aber Vorteile. Da ich auch Java entwickle, bin ich natürlich nicht so ganz unparteiisch. Verständnis habe ich aber durchaus dafür, dass insbesondere in den Standard-Funktionen eher auf false als Rückgabe gesetzt wird, um die Nutzung zu vereinfachen.

Tipp:
Folgende Regeln sollte man im Umgang mit Exceptions beachten:

  • lokale Behandlung
  • Wird eine Exception gefangen und keine Information weitergegeben, so ist diese Exception ins Logfile zu schreiben
  • Keine Gott-Exceptions nutzen, Fehlerklassen definieren

35 Kommentare

  1. Ein weiteres Problem von false als Fehlermeldung ist, wenn false auch ein gültiger Rückgabewert ist. Bei einigen PHP-Funktionen ist das leider so – das macht das abfangen von Fehlern schwierig…

    EIn Beispiel ist apc_fetch (http://de3.php.net/manual/en/function.apc-fetch.php) da kann (bzw sollte) man – quasi als workaround – den 2. Parameter als Referenz übergeben, um zu sehen ob die Abfrage geklappt hat oder nicht. Nicht gerade eine saubere Lösung wie ich finde…

    Antworten
  2. Ich denke, es geht dabei nicht bloß um Fehlerbehandlung, sondern die Rückgabe von FALSE statt beispielsweise eines numerischen Wertes dient auch bisweilen dazu, mit einer Funktion zusätzliche Informationen zu ermitteln.

    Beispiel strpos – die Funktion kann nicht nur die Position eines Strings in einem anderen ermitteln, sondern auch, ob dieser überhaupt vorkommt. In diesem Sinne ist FALSE also kein Fehler.

    Das macht das Zurückgeben von FALSE im Fehlerfall allerdings noch schlimmer. Das von dir erläuterte Fazit, saubere Exceptions (sprich: spezielle Exceptions) zu werfen, unterschreibe ich sofort.

    Antworten
  3. @Timo: einverstanden. Auf der anderen Seite könnte man aber auch -1 zurückgeben. False ist zugegebenermaßen eigentlich schöner, sorgt aber dafür, dass der Rückgabetyp mixed ist. Und false == 0 ist auch eine mögliche Fehlerquelle. Um strpos PHP-weit umzudefinieren ist es aber auch etwas spät und die Kompatibilität würde ganz schön brechen.

    @KingCrunch: Ist aber doch auch nur eine Konvention, oder ist das Vorgehen technisch bedingt?

    Antworten
  4. Exceptions werden in Ausnahmefällen (Fehler) geworfen, dass ein String nicht in einem anderen vorkommt ist garantiert kein Fehler sondern ein ganz normaler Fall von daher wäre eine Exception-Nutzung komplett fehl am Platz.

    Antworten
  5. Ich sehe das wie Tobias. Exceptions, wenn ein Fehler auftritt und ansonsten eben ein beliebiger Wert, der im jeweiligen Kontext Sinn macht.

    Antworten
  6. Also ich will ja nicht strpos mit einer Exception ausstatten – ich muss mir mal vermerken, dass ich Beispiele sorgfältiger auswähle. Insbesondere wenn es Interpretationsspielraum gibt 🙂

    Ich finde übrigens nicht, dass Ausnahme mit Fehler gleichzusetzen ist. Es bedeutet doch vielmehr, dass ausnahmsweise die Erwartung nicht erfüllt wird. Um ein hoffentlich klares Beispiel zu nennen nehme ich offsetGet in einem ArrayObject. Existiert der Offset nicht, so gibt es eine Notice. Hier würde ich aber eine Exception erwarten.

    Hat jetzt weniger mit dem der Frage false oder Exception zu tun, ist aber ziemlich spannend.

    Antworten
  7. False ist das Ergebnis eines mathematischen Ausdrucks. Im Fall von strpos wäre das: Ist Menge N (needle) in Menge S (string) enthalten. Ist sie sie enthalten, ist das Ergebnis wahr, ansonsten falsch. Deswegen prüft man auch besser nicht auf true|false, sondern mit is_numeric. Dann unterläuft einem auch nicht der Fehler mit 0.
    offsetGet ist kein mathematischer Ausdruck, deswegen gibt es hier einen Fehler. Wobei eine Notice ja im eigentlichen Sinn noch kein Fehler ist. Und warum sollte es eine Exception und keine Notice sein? echo $a; erzeugt auch eine Notice und keine Exception wenn $a vorher nicht definiert wurde. Die Methode heißt nun einmal offsetGet und nicht issetOffset.

    Antworten
    • offsetGet ist ein mathematischer Ausdruck. Ein Array ist nämlich grundsätzlich eine surjektive Funktion. Greife ich mit offsetGet auf ein Element zu das nicht in der Definitionsmenge enthalten ist, so ist das ein Fehler. Und darüber muss ich informiert werden. Da wir nicht typisiert sind, muss man einen Fehler so definieren, dass dieser nicht in der Zielmenge auftreten kann. null, false dürfen in der Zielmenge auftreten, daher scheiden diese aus. Übrig bleibt nur eine Exception. Das wäre meine Begründung, weswegen eine Exception angebracht wäre. 😉

  8. Meiner Meinung sind Exceptions sehr nützlich. Die SPL Erweiterung von PHP (default ab PHP 5.3) unterteilt Exceptions in zwei Grupper ( http://www.php.net/~helly/php/ext/spl/classException.html ). Der Fall der hier diskutiert wird, sind aus meiner sicht LogicExceptions. Somit kann bei der Eingabe oder Übergabe von Parametern an die Klasse unterschieden werden, ob der Input inkorrekt war oder es zu einer RunTimeException gekommen ist. Ralph Schindler hat einen sehr guten Artikel verfasst, der die Exception Klassen unter PHP 5.3 genauer betrachtet und Namespaces einbezieht. Sehr lesenswert: http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3

    Exception können durch phpdoc Annotatitons vermerkt werden. So kann die Methode den Rückgabewert void haben und die möglichen auftretenden Exceptions speziell deklariert werden.

    Antworten
  9. IMHO muss man da schon differenzieren.
    Funktionen wie isObjectActive gibt entweder TRUE oder FALSE zurück, Funktionen wie getObject können Exceptions werfen.

    Eigentlich ist es ganz einfach: Solange FALSE Teil der Antwort sein kann, braucht man keine Exception. Eine Exception ist eine Ausnahme, also genau der Fall, der nicht auftreten darf, aber trotzdem auftreten _kann_ (warum auch immer).

    Die Idee mit den Integerwerten als Rückgabe kenne ich noch aus dem Jahr 2000 aus C++ und Delphi Zeiten, positive Werte waren „ok“ und sagten was zum Status aus, negative Werte hießen „es ging was schief und zwar das-und-das (je nach Wert)“. Diese Integer konnte man auch in jede Ebene zurückgeben, was eine „Fehlerbehandlung“ überall ermöglichte. Einen Unterschied gibt es jedoch: Während es (im Frontend) dem Integer total egal ist, ob der Entwickler eine Nachricht an den Benutzer ausgibt, „zwingt“ die Exception den Entwickler, sich um die Ausnahme zu kümmern und diese auch zu behandeln (und wenn es nur die Info an das Frontend ist).

    Insofern plädiere ich für Exceptions, falls etwas (Rück-)gemeldet werden soll, was nicht in die normale Antwortmenge passt.

    Antworten
  10. Hi,

    Exceptions müssen allerdings nicht unbedingt immer Fehler sein, sonst würden sie wohl eher Error o.ä. heißen. Exceptions sind Ausnahmen, die in einem Programm auftreten können. In der Regel handelt es sich natürlich um Fehler. Muss aber nicht immer der Fall sein. Ein Beispiel wäre eine Reservierungssoftware, bei der jeder 1000te Reservierer einen Preis gewinnt. Die Ausnahme wäre in diesem Fall „Aktueller Besucher ist der 1000te“. Das ist sicherlich kein Fehler, aber eine Ausnahme und daher könnte in diesem Fall eine UserWonAPresentException geworfen werden(die nicht unbedingt behandelt werden muss) .

    Außerdem können in bestimmten Situationen bzw. Betrachtungslevel Fälle als Ausnahmen bzw. normal behandelt werden. Beispielsweise sollte für eine Datenbank-Connection Klasse oder Komponente der Fall, dass keine Verbindung zur Datenbank hergestellt werden kann, nicht unbedingt ein Fehler oder eine Ausnahme sein. Aus Sicht der Anwendung aber schon, da z.B. keine Benutzerdaten aufgerufen werden können. Daher wäre wohl das Folgende (semantisch) besser, als direkt eine DBException zu werfen:
    if (!dbConn.connect()) {
    throw new NoUserDataAvailableException();
    }

    Schöne Grüße

    Pedram

    Antworten
  11. Ich gehe der Regel nach die ich in einem Buch(*) mal gelesen habe.

    Jede Operation stellt eine Funktionalität (möglichst durch eine fest definierte Schnittstelle) zur Verfügung.
    Diese Schnittstelle beinhaltet einen Kontrakt, der aussagt welche Voraussetzungen sie erfüllt haben will, und welche Zusicherungen (Funktionalität) sie dafür liefert. Wenn die Funktion ihren Kontrakt nicht erfüllen kann, muss sie eine Exception, also eine Ausnahme (des Kontraktes) werfen, welche dem Grund entspricht, warum sie ihren Kontrakt nicht erfüllen kann.

    Wenn wir zum Beispiel eine Funktion schreiben welche eine Datei öffnen soll, so besteht der Kontrakt aus der Vorbedingung, dass ein Pfad zu einer existierenden Datei mit Leseberechtigung übergeben wird und der Zusicherung, dass sie eine offene lesbare Resource auf diese Datei zurückgeben wird. Wenn der Aufrufer die Vorbedingungen dieses Kontraktes nicht erfüllt, also der Pfad falsch ist, die Datei nicht existiert oder keine lese-Rechte hat, wird ein normaler Fehler („trigger_error()“) zurückgegeben, da sich im aufrufer-code offensichtlich ein Fehler befindet. Wenn aber der Aufrufer sich an die Vorbedingungen gehalten hat, und nun ein Defekt auf der Festplatte auftritt welcher das Öffnen der Datei unmöglich macht, muss diese Funktion eine Ausnahme werfen die besagt, dass die Festplatte fehlerhaft arbeitet (z.B.: eine HardDriveDefektException, oder einfach IOException), mit welcher der Aufrufer nun umgehen kann. Dadurch ermöglicht man dem Programm, sich in einen sicheren Zustand zu bringen und dann kontrolliert herunterzufahren oder ein Teilprogramm neu zu starten, anstatt einfach abzustürzen und eventuell invalide oder schlimmer noch falsche Daten zurück zu lassen.

    Meiner Meinung nach ist ein „return false“ als Fehler-Angabe (nicht zu verwechseln mit negativ-Rückgabe bei z.B.: nicht finden eines String-Vorkommens) immer falsch. Denn wenn der Kontrakt vom Aufrufer nicht eingehalten wurde liegt fehlerhafter Code vor welcher mit trigger_error gemeldet werden muss, und wenn eine Ausnahme vom Kontrakt gemacht werden muss (wie im Fall der defekten Festplatte) muss eine Exception geworfen werden.

    * = http://openbook.galileocomputing.de/oop/oop_kapitel_07_006.htm#mjec595d98347a935335b712f91956664c

    Antworten
  12. @Tobias: Ausnahmen sind keine Fehler. Diese Erkenntnis ist sehr wichtig, um Exceptions zu vestehen. Ausnahmen sind nicht berücksichtigte Fälle, die eintreten können. Werden mehr Fälle berücksichtigt, braucht man auf weniger Ausnahmen reagieren und bekommt somit spezifischere Informationen über die Ausnahme.
    Zu deinem Beispiel mit dem String kommt es darauf an, was man erwartet. Wenn _immer_ ein String in einem anderen vorhanden sein muss, ist es eine Ausnahme, wenn das nicht der Fall ist. Das weitere Vorgehen kann sich mit weiteren Ausnahmebehandlungen decken, weshalb ein Exception-Typ für z.B. Fehleingaben durchaus sinnvoll ist und den Benutzer zur erneuten Eingabe auffordert. Der Fehlertext kommt von der Exception. Dein Programmieraufwand ist minimiert.

    Antworten
  13. Don Zampano

    03/03/2011 @ 12:57

    @addiks
    „Meiner Meinung nach ist ein “return false” als Fehler-Angabe (nicht zu verwechseln mit negativ-Rückgabe bei z.B.: nicht finden eines String-Vorkommens) immer falsch. “

    Exakt, Vertrag bzw. Kontrakt ist das Zauberwort.
    Ausnahmen sind boolsche Funktionen (isA, hasA, canB, satisfiesD), die man aber sowieso nur sparsam einsetzen sollte.

    Denn generell sollte man ja sowieso nur Methoden aufrufen, die etwas tun sollen und nicht auch noch fragen, ob sie es gemacht haben. Wäre ja sonst ein Verstoß gegen SRP (mach das UND sage mir ob es erfolgreich war oder nicht). Subtil und doch offensichtlich.
    Also braucht man auch kein boolean als Bestätigung oder Verneinung.

    Daneben klären sich durch defensive Programmierung sowieso die meisten Fälle von selbst, ob Fehler oder Exception.

    @Pedram
    Also, das bei „jeder 1000te Reservierer einen Preis gewinnt.“ der Eintritt dieses Ereignisses eine Ausnahme sein soll, ist wohl eher ein Scherz, oder? Denn du definierst ja genau diese Anforderung und erwartest sie ja auch. Vergewaltige keine Exceptions, um Ersatz für vorhersehbare (!) Events zu sein.

    @KingCrunch
    „Zur Erinnerung: Core-Komponenten dürfen keine Exceptions werfen.“

    Und auch nicht Manager-, Base-, Basic-, Fundamental-, Existential-, Universal-Komponenten. Bei meinen Hardcore-Komponenten bin ich aber nicht so sicher…

    Antworten
  14. @Don Zampano
    Nein, das ist kein Scherz. Und schon gar nicht eine Vergewaltigung der Exceptions. Schöne Wortwahl übrigens. Was vorhersehbare Events sind, das musst du mir bitte erklären. Ist denn die Möglichkeit, dass eine Datei nicht gefunden werden kann, nicht auch ein vorhersehbares Event? Es ist genau so unklar, wann eine Datei nicht gefunden werden kann, wie der Fall, wann der 1000te Besucher eintrifft. Ausnahmen haben nichts mit Vorhersehbarkeit oder Nichtvorhersehbarkeit zu tun, sondern mit Ausnahmefällen. Sonst würden sie „Nichtvorhersehbare Ereignisse“ o.ä. heißen.

    Mein Beispiel mit dem 1000ten Besucher kann durchaus als Exception behandelt werden. Sollte es wahrscheinlich sogar. Denn es ist eine Ausnahme, dass ein Besucher einen Preis gewinnt. Diese Ausnahme befindet sich aber wohlbemerkt auf ANWENDUNGSEBENE. Aus der Sicht der Anwendung ist es alles andere als der Normalfall, dass jemand gewinnt. Und wenn wir anstatt des 1000ten den millionsten Besucher nehmen würden, oder um es, wie du sagst nicht mehr „vorhersehbar!“ zu machen, einfach durch einen Zufallsgenerator bestimmen, ob jemand gewonnen hat, ist es dann eine Ausnahme?

    Ausnahmen werden fälschlicherweise oft nur im Kontext von technischen Problemen betrachtet (keine Verbindung zur DB etc.). Aber sie existieren auf Anwendungsebene genauso. Das Beispiel des gewinnenden Besuchers kann dahingegen erweitert werden, dass es X-beliebige Ausnahmefälle gibt, wann jemand gewonnen hat. Diese können durch eigene Exception-Klassen behandelt werden. Beispielweise (1000te Besucher, 123ter Besucher heute, 3ter Besucher mit dem Anfangsbuchstaben Y im Namen, usw.) Das sind alles Ausnahmen auf Anwendungsebene, die unterschiedlich behandelt werden können. Und ja, sie sind alle vorhersehbar, aber dennoch, wann treten sie ein? Vielleicht kommt es nur 1mal im Monat vor, dass es überhaupt 123 oder mehr Besucher am Tag gibt. Vielleicht aber auch nie.

    Schöne Grüße

    Pedram

    Antworten
  15. @Pedram: Wenn ich den 1000sten Besucher beglückwünschen will, dann würde ich dieses „Ereignis“ auch im System als „Event“ bekanntgeben. Sowas über Exceptions zu lösen finde ich auch nicht besonders sinnvoll.

    Antworten
  16. Don Zampano

    03/03/2011 @ 16:43

    @Pedram

    Ouch, es scheint leider wirklich dein Ernst zu sein…

    Abgesehen von den rechtlichen Problemen („Denn es ist eine Ausnahme, dass ein Besucher einen Preis gewinnt.“ …lol) ist deiner Argumentation nach alles eine Ausnahme.

    Ich bin jemand, der fast nichts auf technischer Ebene betrachtet, bei mir gibt es keine Datenbank, kein Logging, keine Sessions, wenn ich meine BL modelliere.
    Denn fast jede BL, die ich baue, würde auch ohne dies funktionieren müssen und hat es vor dem Internet auch.

    Es ist alles „unnormal“, was du nicht als Teil deiner Geschäftslogik ansiehst. Ist dies auch der Fall, dass nie jemand gewinnen darf, dann würde ich sagen, dass diese Logik für den A*** ist. Soll ein Dateimanager eien vermeintlich vorhandene Datei holen und findet sie nicht, dann ist es eine Ausnahme. Denn sonst wäre sie nicht vermeintlich vorhanden (vllt. liegt der Fehler woanders?)

    Es ist eigentlich recht einfach:
    Entwickelst du testgetrieben, so hast du ja bestimmt auch einen Testcase testWinGetsDistributedOnReachingEntrantCriteria oder so. Was willst du sonst testen? Das das Gewinnspiel startet, jo. Und sonst?

    Defensive und testgetriebene Entwicklung berücksichtigt Grenzwerte und Aäquivalenzklassen. Also definierst du z.B. einen Zeitpunkt, an dem ein bestimmtes Limit erreicht ist (1000er, 1000000er Gewinner) und bastelst dafür die Testcases. Ist doch eigentlich normal, oder nicht?

    Sonst wäre es ja als ob du ein Social Framework bastelst, an dem sich nie ein User anmelden kann, ist ja unwahrscheinlich.

    Entwickler habe meist kein Problem, alles sofort in Code zu materialisieren und irgendwann aufgeschnappte Ideen darin zu verwirklichen.

    Lehn dich mal einen Schritt zurück und überlege, was du da tust/tun willst.
    Ein Gewinnspiel veranstalten, das nie jemand gewinnen wird?
    Wohl kaum.

    Cheers.

    Antworten
  17. @Daniel: Es ist natürlich eine von vielen Möglichkeiten. Und in bestimmten Situationen eine Geschmackssache, wobei Events sicherlich viel mehr Möglichkeiten eröffnen, und wenn diese auch benötigt werden, man sicherlich nicht um Events herum kommt. Jedoch brint natürlich die Lösung über Events oft einiges mit sich: Event Dispatcher, Listener etc. In bestimmten Situation vielleicht mehr als man braucht. Man sollte nicht vergessen, dass Ausnahmen viel mehr sind als das Behandeln von Fehlern. Eine sehr interessante und informative Gegenüberstellung von Ausnahmen und Fehlern ist in dem Buch http://www.amazon.de/Moderne-Software-Architektur-Umsichtig-planen-robust/dp/3898642925 Kapitel 5 nachzulesen. Danach sieht man das ganze vielleicht mit anderen Augen.

    Schöne Grüße

    Pedram

    Antworten
  18. Don Zampano

    03/03/2011 @ 16:53

    @Pedram

    „Was vorhersehbare Events sind, das musst du mir bitte erklären. Ist denn die Möglichkeit, dass eine Datei nicht gefunden werden kann, nicht auch ein vorhersehbares Event?“

    Das erkläre ich dir gerne auch noch.

    Baust du einen Dateimanager, so sind alle Dateien, die dort in der Liste oder per dir angezeigt wreden auch vorhanden. Versuchst du nun eine davon zu öffnen und es kommt „File not found“ ist das eine klare Ausnahme.
    Denn die „BL“ eines Dateimanagers ist u.a. Dateien aufzulisten.

    Gibt dir deine DB oder ein Link eine Referenz zu einer Datei und sie wird nicht geliefert oder ist sie nicht da, heißt das bspw., dass die Lieferanten einen nicht mehr gültigen Verweis darauf haben oder sie inzwischen wirklich gelöscht wurde.
    Das würde ich vorher abfragen, da ich mich auf andere Systeme verlassen muss, die prüfe man sowieso immer sorgfältig.
    Oder aber ich verlasse mich darauf und im Fehlerfall werfe ich eine Exception. Gleiche Story wie beim Dateimanager.

    Es ist eine Frage des Vertrauens und der Verträge (s.o.), was ein Fehler und was eine Ausnahme ist.
    Die Antwort liefert – wie immer – der Kontext.

    Antworten
  19. @Zampano
    Die Rede war nicht von einem Gewinnspiel, sondern von einer Reservierungssoftware. Da ist es durchaus eine Ausnahme, dass jemand gewinnt. Ausnahmen sind nichts Negatives, sie sind einfach Fälle, die selten zu erwarten sind. In einem Gewinnspiel wäre der Fall keine Ausnahme. Wie du schon richtig genannt hast „Geschäftslogik“, DAS ist hier das Wichtige. Daher betonte ich ja: auf ANWENDUNGSEBENE ist es eine Ausnahme. Und in der Anwendung „Gewinnspiel“ ist es KEINE Ausnahme, dass jemand gewinnt (außer vielleicht beim Lotto SixNumbersRightException!?), aber für eine Reservierungssoftware durchaus. Genau da muss differenziert werden.

    Wenn ich davon ausgehe, dass ich mit einem Server eines Clientes kommunizieren muss und dieser mir sagt, unser Server liegt sehr oft lahm (aus welchen Grund auch immer), dann ist es in diesem Fall KEINE Ausnahme (Exception), dass ich keine Verbindung zum Server aufbauen kann. Sowas kann der ExtendedDateimanagerForStupidClient durchaus berücksichtigen. Da ist es vielleicht sogar eine Ausnahme, wenn eine Verbindung hergestellt werden kann. 🙂 Wie man sieht, gibt es kein Schema F, nach dem man gehen kann, genau DAS ist die Herausforderung. Von Fall zu Fall, von Software zu Software kann alles anders sein.

    Ich spreche aus der Sicht eines Java-Entwicklers. Hier wird explizit sehr viel mit Schichten und Ebenen gearbeitet in der Softwarearchitektur. Weiß nicht genau wie es beim Programmieren mit Php ist, aber dürfte nicht viel anders sein. Ich programmiere lediglich kleinere Sachen mit Php.

    Ich kann dir nur ans Herz legen, dass oben genannte Buch bzw. das Kapitel 5 zu lesen. Dann siehst du, dass es durchaus andere interessante Ansätze zum Lösen von Problemen gibt. Das ist ja das Tolle am Softwareengineering und der Informatik im Allgemeinen. Welches Verfahren nun das bessere ist, darüber lässt sich oft streiten.

    Schöne Grüße

    Pedram

    Antworten
  20. Hmm…und warum ist denn das Eintreten des 1000ten Besuchers vorhersehbarer, als das Fehlen einer Datei? Wie Daniel schon sagte, man könnte den 1000ten Besucher als Event implementieren, aber Events sind doch alles andere als vorhersehbar?! Es bleibt auch die Frage, kommt es jemals zu einem 1000ten Besucher? Wenn es vorhersehbar wäre, könnte diese Frage mit ja oder nein beantwortet werden.

    Und wohl bemerkt, in deinem Beispiel der fehlenden Datei handelt es sich ganz klar um einen FEHLER. Der 1000te Besucher ist schlicht und einfach eine AUSNAHME. Das diese beiden Fälle nun technisch mit „Exceptions“ behandelt werden können, ist nun reiner Zufall. Eine Programmiersprache könnte genauso gut zwischen dem Werfen von Errors und dem Werfen von Exceptions unterscheiden.

    Schöne Grüße

    Pedram

    Antworten
  21. @Pedram
    Wirfst du eine Exception, verlässt du ja denn normalen Ausführungskontext. Das würde aber bedeuten das die Reservierung nicht mehr sauber durchgeht.
    Du missbrauchst die Exception, um ein festgestelltes Ereignis (1000te Reservierung), in einen höhren Layer zu propagieren. Das höhrt sich für mich nicht nach sauberen, modularen Desgin an.

    Antworten
  22. Man kann sicher beide Beispiele, sowohl das mit der Datei als auch das mit dem 1000ten Besucher, mit Exceptions behandeln. Aber dann im Kontext des Vertrages (wichtiges Stichwort, der Erwartungswert) vielleicht so:
    Wenn ich erwarte das die Datei, deren Inhalt ich haben möchte, immer da ist und diese dann fehlt, dann ist das eine Ausnahme welche ich mit Exceptions weiter behandeln kann. Im Beispiel des 1000ten Besuchers ist es für mich dann eine Ausnahme wenn ich feststellen kann, dass über 1000 Besuche stattgefunden haben aber der 1000te Besucher nicht benachrichtigt wurde. Denn das bricht meine Erwartungshaltung. Und wichtig sind auch die Möglichkeiten die Exceptions für die Weiterbehandlung bieten.

    Grüße,
    Ronald

    Antworten
  23. @Baran
    Es ist wie gesagt abhängig von der Situation. Wenn der 1000te Besucher zu einer anderen Seite mit vielen Blumen und Goldmünzen und Trompetenmusik weitergeleitet werden soll, anstatt eines einfachen „Willkommen“, dann ist es sogar notwendig den normalen Ausführungskontext zu verlassen. Wenn die Anwendung es nicht verlangt, dass der 1000te Besucher anders (d.h als Ausnahme) behandelt werden soll, dann darf auch keine Exception verwendet werden.

    Es ist natürlich nicht immer notwendig und schon gar nicht möglich für jede Ausnahmesituation Exceptions zu verwenden. Mein Anliegen war es klar zu machen, dass Ausnahmen nicht gleich Fehler, kritische Situationen oder sonst etwas Schlechtes sein müssen. Im Buch von Johannes Siedersleben wird unterschieden zwischen „normalen“ und „abnormalen“ Fällen OHNE jede Wertung. Abnormale Fälle sind einfach Fälle, bei denen man ausgeht, dass sie nicht oft vorkommen.

    Ein schönes Beispiel ist die Java-Exception InterruptedException, die folgendes macht:
    „Thrown when a thread is waiting, sleeping, or otherwise paused for a long time and another thread interrupts it using the interrupt method in class Thread.“
    Diese Exception ist nichts Böses, kommt sogar gar nicht soo selten vor. Klar, könnte wohl mit Events gelöst werden, aber das Team von Sun hat sich für eine Exception entschieden. Was die Java-Programmierer sogar wahrscheinlich freut, weil sie nichts anmelden, delegieren usw. müssen. Ein throw new… reicht schon.

    Der Grund warum Zampano (und vielleicht viele andere :)) meinen Post als „Scherz und Vergewaltigung“ gesehen hat, liegt lediglich daran, dass Exceptions oft und in der meisten Literatur in einem Atemzug mit Problembehandlung genannt werden. Oder zumindest beschäftigen sich Beispiele über die Arbeitsweise von Exceptions meistens mit Fehlerfällen: NullPointerException, IllegalArgumentException, CastCastException uvm.

    Schöne Grüße

    Pedram

    Antworten
  24. Die Sache mit dem 1000ten Besucher sehe ich folgendermaßen: Wenn dieses Ereignis den Abbruch des aktuellen Programmflußes nach sich zieht, ist eine exception okay. Sollte aber nur eine spezielle Mitteilung über das Ereignis an ein weiteres Modul gesendet werden, benutzt man bzw ein Event, oder von mir aus auch ein Callback (um noch ein Konzept in den Raum zu werfen).

    Die Diskussion an sich finde ich schon mal sehr spannend und gewinnbringend!

    Antworten
  25. @Ronald
    Für mich hört sich die Sache mit dem Kontrakt ganz klar nach CheckedExceptions an, d.h. Exceptions die abgefangen werden müssen. „Hälst du den Vertrag nicht ein, müssen wir andere Wege einleiten.“ Ich habe das Galileobuch nicht gelesen, aber kann mir nicht vorstellen, dass damit auch RuntimeExceptions gemeint sind, d.h. Exceptions die man behandeln kann, aber nicht muss. In vielen Programmiersprachen gibt es ausschließlich eine von beiden Typen.

    Exceptions ausschließlich abhängig von nicht eingehaltenen Kontrakts zu machen, der Gedanke gefällt mir nicht. Das ist alles zu sehr auf Methoden- oder Objekt-Ebene. Aber Exceptions können durchaus mehr betreffen als den Vertrag zwischen zwei Methoden, z.B. das Runterfahren des Programms (um wieder beim Fehlerfall zu sein). Was ist wenn die selbe Methode von vielen anderen Methoden verwendet wird. Kann sie mit allen einen anderen Vertrag eingehen oder müssen dafür neue Methoden erstellt werden? Ich verspreche, ich werde mir das Kapitel dazu durchlesen. Man weiß ja nie. Wobei, das hört sich wieder nach eine Fehlerbehandlungsausnahme an.:)

    Schöne Grüße

    Pedram

    Antworten
  26. Vielleicht kann man sich ja mal darauf einigen: Exceptions sind Teil einer Kontrollstruktur die (unter anderem) mit Try-Catch zusammen arbeiten.

    Antworten
  27. Don Zampano

    05/03/2011 @ 21:38

    @Pedram
    „die meiste Literatur“ interessiert mich nicht.
    Die meiste Literatur über OOP (zumindest in PHP) behandelt auch ständig zu sehr Vererbung anstatt Komposition, als ob es noch ein guter Ansatz sei; oder Singletons als ernst zu nehmendes Pattern.

    Wenn die Diskussion von allen so geführt wird wie bisher, können Exceptions und normale Fehler wechselseitig und beliebig mal so und mal so benutzt werden. Oder nur technisch oder nur logisch betrachtet werden.

    Mein Hauptargument, wann was warum per Exception behandelt werde sollte, ist nach wie vor der testgetriebene Ansatz. Ich definiere Invarianten, Grenzwerte und Bedingungen, die eingehalten werden müssen (oder auch können) so fokussiert wie möglich, vorgegeben wird dies von den Anforderungen und sonst nichts.

    Das behandle und fange ich mit normalen Fehlern ab. Alles darüber hinaus sind Ausnahmen. Und zwar so lange, bis diese Ausnahmen nicht Teil der Bedingungen werden (und vice versa).

    So muss ich mir nicht in jedem Fall Gedanken machen, warum ich nun Exceptions werfen soll oder nicht.
    Meine Anforderungen sagen mir das und nicht irgendwelche mittelmäßige oder fehlerbehaftete Literatur und schon garnicht irgendwelche technischen Einschränkungen.

    Cheers

    Antworten
  28. @Pedram

    Dein Gewinner-Beispiel gefällt mir nicht. Exceptions sind für den Fehlerfall vorgesehen, nicht als Event-Signalisierung, und ich will auch begründen, warum.

    Angenommen, jeder 1000. User kriegt beim Anmelden einen Bonuspreis. Würdest du das mit einer Exception lösen wollen, dann müsstest du irgendwo in deinem Code ja sowas tun:

    throw new UserGetsBonusException();

    Das Problem: Mit diesem Werfen verlässt du den Programmfluss an dieser Stelle und springst zurück zu irgendeiner auf einer höheren Ebene befindlichen try/catch-Konstruktion, und dort sofort in den im catch-Block gegebenen Code.

    Du brichst also jeglichen normalen Code-Fluss, der für das Anmelden des Users zuständig ist, ab. Und das halte ich für unmöglich programmierbar, dass der Code in beiden Fällen (Gewinner und kein Gewinner) dann noch sinnvoll funktioniert.

    Denn in der Regel wird man die Methode, die letztendlich auch die Bonus-Exception wirft, ja so aufgerufen haben, dass sie einen Rückgabewert liefert. Wir haben also z.B. mindestens sowas dort stehen:

    return $db->insertId();

    Rufen wir das return vor dem Werfen der Exception auf, kriegt niemand den Bonus. Werfen wir vor dem Return die Exception, kriegt der normal funktionierende Anmeldecode die User-ID nicht mehr.

    Deshalb sind Exceptions in PHP wirklich nur für Fehlerbehandlung sinnvoll. Für die Behandlung solcher Bonus-Geschichten würde man im Zweifel sowas wie einen Observer oder Events ausprogrammieren. Dann wird das Speichern des neuen Users halt etwas länger dauern, weil auch noch der Bonus behandelt wird, aber es würde am regulären Programmfluss des Speicherns nichts geändert.

    Antworten
  29. @Sven
    Genau, die Exception führt zum Verlassen des normalen Programmflusses. Wenn der normale Programmfluss nicht verlassen werden soll, darf ich natürlich auch keine Exception benutzen. Das hatte ich hier https://www.phpmonkeys.de/2011/03/02/exception-werfen-oder-false-zurueckgeben/comment-page-1/#comment-789 beschrieben.

    So wie in deinem Beispiel beschrieben, kann keine Exception verwendet werden. Das ist richtig. Das Problem ist aber weniger die Exception, sondern die Implementierung. Man könnte außerdem der Exception die insertId mitgeben. Die Exception kann natürlich an irgendeiner höhe liegenden try/catch-Konstruktion geschickt werden, aber dazu zwingt dich niemand. Die Exception kann/sollte von der selben Methode behandelt werden, die db->insertId() aufruft. Zum Beispiel:

    1
    2
    3
    4
    5
    6
    7
    8
    
    try {
      $userId = db->insertId();
      showWelcomePageForNormalUser($userId);
    }
    catch(SpecialUserException e) {
      $userId = e.getId();
      showWelcomePageForSpecialUser($userId);
    }

    Ich wage zu behaupten, dass dies verständlicher und sehr viel unaufwendiger ist als die Implementierung mit Observer etc. Wie kann ich über Observer den normalen Programmfluss so schön und einfach abbrechen und einen speziellen Fluss wählen? Was gibt es gegen diesen Code-Ausschnitt einzuwenden? Es ist super zu lesen, und ich muss nicht irgendwelche Listener suchen, die „irgendwo“ „irgendetwas“ machen.

    Nochmal: Exception != Fehler (siehe mein Java-Beispiel). Aber klar ist, Fehler bilden die meisten Ausnahmen.

    Schöne Grüße

    Pedram

    Antworten
  30. @pedram

    Deine Idee verstehe ich schon. Find ich aber umständlich.
    Wär das nicht einfacher und billiger?

    1
    2
    3
    4
    5
    6
    
      $userId = db->insertId();
      if($userId != 1000) {
         showWelcomePageForNormalUser($userId);
      } else {
         showWelcomePageForSpecialUser($userId);
      }

    Ich vermute einfach mal dass der Vergleich so passen würde. Dann würde es Performance bringen, weil zum einen die erste Funktion nicht ausgeführt werden muss und zum anderen das Werfen von Exceptions und das Abfangen vermieden wird (kostet zwar nicht viel, aber kostet auch, die IF Abfrage ist auf jeden Fall billiger).

    Antworten
  31. @HD81
    Es sollte eigentlich nur ein kleines Beispiel sein, um zu zeigen, dass es durchaus mit Exceptions funktioniert und auch gut lesbar ist.:) Nun ist es natürlich so, dass es immer viele Möglichkeiten gibt ein Problem zu lösen. Mache ich es in diesem Fall wie du, dann ist es bestimmt besser. Aber was machst du, wenn dieser SpecialUser mehrere unterschiedliche Methoden interessiert? (Das ist nicht immer ein Fall für Observer) Willst du dann bei allen ein if ($userId != 1000) hinschreiben? Oder, ein SpecialUser ist nun auch jemand dessen Name mit „X“ beginnt oder am 9.9.1999 Geburtstag hat. Willst du das wirklich überall hardcoden? Bestimmt nicht. Du hättest dann die Logik eines speziellen Users überall rumliegen, ändert sich diese, dann viel Spaß. Oder sogar noch schlimmer: dir fehlt plötzlich die Information, ob jemand ein SpecialUser ist, weil du in der Datenbank nachschauen müsstest, ob bestimmte Bedingungen gelten. In meiner Variante sagst du einfach „Versuch mal den normalen Weg zu gehen, aber sag mir bescheid wenn es sich um einen speziellen User handelt. Was ein spezieller User ist bzw wann es sich genau um einen solchen handelt, interessiert mich nicht. Wirf einfach eine Exception wenn es der Fall ist.“

    Abgesehen davon, deine Variante führt – angenommen der Check, ob es sich um einen speziellen User handelt, ist wirklich so einfach – wieder zu der Frage, ob wir false zurückgeben sollen oder doch lieber eine Exception werfen. Allgemeiner gesagt, soll die aufrufende Methode anhand des Rückgabewerts entscheiden, ob es sich um eine Exception bzw. Abnormalität handelt? Bei einer Ja/Nein Frage geht es vielleicht, da eine Methode nun mal nur einen Rückgabewert hat. Wird es komplexer muss von diesen beiden Möglichkeiten definitiv die mit Exceptions gewählt werden, da man von diesen mehrere und unterschiedliche werfen kann und nicht nur auf eins beschränkt ist.

    Antworten

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.