dedlfix: Wann wirft man eine Exception

Beitrag lesen

Tach!

Mein Problem ist glaube ich eine Entscheidung zu treffen ob ich Exceptions wirklich komplett in mein System einbaue. Meine Befürchtung ist, dass der Code dadurch so sehr aufbläht wird, dass er nicht mehr wartbar wird.

Da wirst du erstmal Erfahrung aufbauen müssen, was üblicherweise durch Fehlschläge und Umbarbeiten beim Verlassen des Holzweges begleitet wird.

Etwas läuft schief, liegt es am Hauptsystem oder ist eine der tausenden catch Blöcke dafür verantwortlich? Und was machen die catch Blöcke überhaupt? Sind die in der Large eine wirkliche Alternative zu erstellen (z.B. Platzhalterbild laden, wenn kein Hauptbild gefunden wurde) oder machen die Catchblöcke nichts weiter als die Exception zu loggen und das Programm ab zu brechen.

Das kommt drauf an. Wenn es eine Lösung für auftretende Probleme oder auch nur eines speziellen Problems gibt, dann kann man das im catch angehen. Wenn nicht, delegiert man das nach oben weiter. Ob es weiter oben korrigiert werden kann, ist nicht mehr Sache der Subroutine.

Wenn dem so ist, dann schaffe ich das auch ohne Exceptions. Wie schon an anderer Stelle gesagt wurde, am Ende wird der Programmablauf beendet und eine Fehlermeldung wird präsentiert oder eine bestimmte Defaultseite wird geladen.

Das muss, wie schon gesagt, nicht unbedingt sein. Wenn es stattdessen was sinnvolles für den Anwender gibt, sollte man das nehmen und nicht nur eine Meldung bringen. Und selbst die Meldung sollte dem Anwender etwas nützliches sagen und nicht davon ausgehen, dass das nur der Administrator zu Gesicht bekommt.

Ich habe hier eine Klasse von der wird sehr viel abgeleitet. Wenn ich in diese Klasse eine Exception in den Konstruktor baue, muss ich quasi bei jeder Funktion oder Methode im kompletten System ein try catch block einbauen. Und da frage ich mich ob es die Mühe wert ist. Vor allem würden Fehler dieser Klasse sofort beim testen auffallen.

Was würdest du denn ohne Exception machen? Da kannst du doch auch nicht eventuelle Fehler unbeachtet lassen und einfach weitermachen.

Auf der anderen Seite empfinde ich es als guten Programmierstil Exceptions zu werfen. Denn damit bietet man dem Aufrufer die Chance einen Plan B zu entwickeln. Da ich aber gleichzeitig der Aufrufer bin und weiß, dass es keinen Plan B gibt (zumindest nicht für diese wichtige Klasse), ist die Überlegung natürlich nur logisch ob ich überhaupt Exceptions brauche.

Vielleicht ist es dann nicht verkehrt, deine Gedanken auch mehr der B-Planung zu widmen. Als Programmierer ist man gelegentlich betriebsblind. Ideal ist es, wenn man auch noch selbst der Anwender ist, dann weiß man schon besser, was man benötigt.

Wieauchimmer, mal ein Vergleich zwischen herkömmlicher und Exceptions-Fehlerbehandlung für eine 08/15-Datenabfrage. Ich nehme mal bewusst die mysql-Extension und nicht das objektorientierte mysqli (oder gar PDO, das man auf Exception-Werfen statt herkömmmlicher Fehlermeldungen umstellen kann), weil man mit den herkömmlichen Funktionen und ihrer Art mit Fehlern umzugehen immer wieder in Berührung kommt, egal, wie objektorientier man sein eigenes System baut.

$conn = mysql_connect(...);  
mysql_select_db('db', $conn);  
mysql_set_charset('...', $conn);  
$sql = "SELECT ... FROM ...";  
$res = mysql_query($sql, $conn);  
while ($row = mysql_fetch_assoc($res)) {  
  ...  
}  
mysql_close($conn); // braucht man nicht unbedingt

Dieser Ansatz ist schön kompakt. Soweit so schlecht, ohne Fehlerbehandlung ist das natürlich Mist.

$fehler = false;  
if ($conn = mysql_connect(...)) {  
  if (mysql_select_db('db', $conn)) {  
    if (mysql_set_charset('...', $conn)) {  
      $sql = "SELECT ... FROM ...";  
      if ($res = mysql_query($sql, $conn)) {  
        while ($row = mysql_fetch_assoc($res)) {  
          ...  
        }  
      } else {  
        $fehler = true;  
        // Reaktion auf Abfragefehler  
      }  
    } else {  
      $fehler = true;  
      // Reaktion auf Set-Charset-Fehler  
    }  
  } else {  
    $fehler = true;  
    // Reaktion auf DB-Select-Fehler  
  }  
  mysql_close($conn);  
} else {  
  $fehler = true;  
  // Reaktion auf Verbindungsfehler  
}  
  
if ($fehler)  
  // ...

Mit voll ausgebauter Fehlerbehandlung wird es nicht übersichtlicher, und dabei gehe ich mal davon aus, dass es für den Anwender unwichtig ist, wo genau der Fehler auftrat, weswegen ich nur ein Flag setze, das hinterher ausgewertet wird. Als "Reaktion" kommt dann im Prinzip nur noch ein Logging für den Administrator in Frage. Man kann das etwas weniger verschachteln, müsste dann aber mit goto ans Ende springen.

if (!$conn = mysql_connect(...)) {  
  $fehler = true;  
  // Reaktion auf Verbindungsfehler  
  goto ende;  
}  
if (!mysql_select_db(...))  
...  
  
ende:  
if ($fehler)  
  // ...

Nun das Ganze mit Exceptions.

try {  
  if (!$conn = mysql_connect(...))  
    throw new Exception('Verbindungsfehler');  
  if (!mysql_select_db('db', $conn))  
    throw new Exception('Select-DB-Fehler');  
  if (!mysql_set_charset('...', $conn)) {  
    throw new Exception('Set-Charset-Fehler');  
  $sql = "SELECT ... FROM ...";  
  if (!$res = mysql_query($sql, $conn))  
    throw new Exception('Abfragefehler');  
  while ($row = mysql_fetch_assoc($res)) {  
    ...  
  }  
  mysql_close($conn);  
} catch {  
  // Reaktion auf Fehler  
  // - Alternative für den Anwender  
  // - Meldungstext für den Admin loggen  
}

Hier sind immer noch eine Menge if-Anweisungen drin, was aber an PHP liegt. Die ifs übersetzen sozusagen die herkömmlich gemeldeten Fehlerzustände in Exceptions. Wäre PHP eine vollständig objektorientierte Sprache, würden die Methoden des DB-Objektes selbständig Exceptions werfen und der Code innerhalb des try sähe so aus wie eingangs der kompakte. In diesem Fall bringen Exceptions wieder etwas Übersicht in den Code.

dedlfix.