Fehler treten ständig und überall auf – wer sich regelmäßig mit Softwareentwicklung beschäftigt, dem ist diese (unangenehme) Tatsache sehr bald nur allzu bewusst. Seltsamerweise schlägt sich dieses Bewusstsein oft nicht im geschriebenen Code nieder. Das kann viele Gründe haben: Zeitdruck, mangelnde Sorgfalt oder schlichtweg fehlende Aufmerksamkeit für das Problem.
Die Folge: Falls Funktionalität implementiert werden soll, die auf Komponenten mit nachlässiger Fehlerbehandlung zurückgreifen muss, so ist die Möglichkeit zu einer korrekten Fehlerbehandlung bereits verbaut. Das verleitet dazu, dieses Thema auch bei der Weiterentwicklung zu vernachlässigen. So können Systeme entstehen, die sich aus Sicht der Anwender und Entwickler völlig unberechenbar verhalten, falls auch nur eine Kleinigkeit schief geht – und wie jeder in der Softwareentwicklung weiß, ist das nun mal leider der Regelfall. Dabei kann man diese Probleme recht einfach vermeiden, wie ich an einigen einfachen Beispielen zeigen möchte.
Exceptions sind für Ausnahmen da
Exceptions sind ein Konzept zur Fehlerbehandlung – und für nichts anderes. Trotzdem stößt man häufig auf Methodensignatur der Art
public List<Foo> getFoosBySomeProperty(List<Property> properties) throws NoElementFoundException
Das Problem an dieser Methode ist, dass sie den Konsumenten dazu zwingt, eine Fehlerbehandlung zu implementieren, selbst wenn nie ein Fehler vorliegt. In diesem Fall sollte der Aufrufer besser eine leere Liste bekommen.
Aber was ist, wenn keine leere Datenstruktur zurückgegeben werden kann, sondern der Rückgabetyp ein atomarer Wert ist?
Eine Möglichkeit ist die Rückgabe von null/nil oder wie auch immer „nichts“ in der jeweiligen Sprache definiert ist. Allerdings birgt dies immer die Gefahr, dass der Client durch eine Nachlässigkeit versucht, auf der Rückgabe Methoden auszuführen, was zu einem nicht mehr sinnvoll behandelbaren Fehler führt. Als Alternative bietet sich das Special Case Pattern bzw. Null Object an.
Error Codes haben ihre Existenzberechtigung verloren
Falls keine Exceptions zur Verfügung stehen, können Fehler nur durch bestimmte Rückgabewerte signalisiert werden. In Sprachen ohne Exception-Konzept ist dies die einzige Möglichkeit zur Fehlerbehandlung. Leider wird diese Tradition immer noch aufrecht erhalten, selbst wenn es mittlerweile mit Exceptions hierfür ein einfaches und dediziertes Konzept gibt. Gegen Error Codes spricht so einiges:
- Sie stellen meistens Zahlenwerte dar – und sind als solche nicht sehr sprechend.
- Da sie keine Objektorientierung erlauben, verkompliziert sich der Behandlungscode.
Entweder sinnvolle Behandlung oder gar keine
Ein benötigtes System ist über das Netzwerk nicht erreichbar, die Datenbank weist ein unerwartetes Schema auf, plötzlich ist kein Speicher mehr verfügbar: all das sind Situationen, in denen eine Fehlerbehandlung keinen Sinn ergibt. In diese Kategorie gehören auch Programmierfehler wie die aus Java nur allzu gut bekannten Ausnahmen IndexOutOfBoundsException
und NullpointerException
. Solche Fehler sind schlichtweg nicht behandelbar und sollten deshalb auch nicht behandelt werden. Vielmehr sollte sichergestellt werden, dass alle verfügbaren Informationen über den Fehler aufgezeichnet und dem Nutzer (falls es sich nicht um ein Batch-System handelt) eine verständliche Fehlermeldung angezeigt wird (und das schließt den Stacktrace nicht mit ein… :D).
Hierzu gehört auch, dass der Client klar zwischen behandelbaren und nicht behandelbaren Fehlern unterscheiden kann. Am besten sollte dies durch zwei getrennte Exception-Hierarchien oder zumindest ein Marker-Interface umgesetzt werden, damit beim Aufrufer keine Verwirrung darüber aufkommt, welche Fehlerfälle zu behandeln sind und welche nicht. Obwohl das Konzept von „Checked Exceptions“ vielfach kritisiert worden ist (z.B. hier oder hier), so ist es in diesem Fall nützlich: Es formuliert eine klare Aussage, dass eine Exception auf jeden Fall zu behandeln ist.
Informationen dürfen nicht zurückgehalten werden
Egal, wer den Fehler behandelt (die Software selbst, Anwender, Systemadministratoren oder Entwickler): sie alle sind darauf angewiesen, dass die Fehlerbehandlung nicht durch das Zurückhalten von Informationen erschwert bzw. unter Umständen unmöglich gemacht wird. Der Klassiker ist die Fehlermeldung „Die Datei xxx kann nicht gefunden werden“. Hier fehlt der Hinweis, wo und ggf. warum diese gesucht wurde.
Aufräumen von Ressourcen
Falls Ressourcen akquiriert wurden, ist sicherzustellen, dass diese auf jeden Fall wieder freigesetzt werden. Kein Grundlagenbuch vergisst, dies zu erwähnen und meist am Beispiel einer Datenbankabfrage das dafür zu verwendende Mittel der Programmiersprache (z.B. finally
in Java) darzustellen. In der Praxis jedoch führen Nachlässigkeiten an dieser Stelle immer wieder zu vermeidbaren Problemen: In Michael Nygard’s „Release it“ wird ein Fall dargestellt, in dem das Informationssystem einer ganzen Airline durch ein solches Problem mehrere Stunden lang nicht funktionsfähig war – der Programmcode rechnete nicht damit, dass das Schließen einer Datenbankverbindung zu einem Fehler führen kann.
Fazit
Fehlerbehandlung ist eine Disziplin, der mindestens genauso viel Beachtung geschenkt werden muss wie der Programmierung der fachlichen Anforderungen. Schließlich bedeutet jeder Fehler einen weiteren Kratzer im Lack der fachlichen Anwendung – dabei hat der Kunde Hochglanz bestellt und verdient!