Writing Code and Other Prose

A blog about software development, electronics, and writing.

Nichts zu finden, ist kein Fehler

Bei der Arbeit mit dem relationalen Datenbanksystem DB2 von IBM bin ich zum wiederholten Mal auf eine hässliche Unsitte aus dem Bereich Schnittstellenentwurf gestoßen: Eine Funktion erzeugt einen Fehler, anstatt ein leeres Ergebnis zurückzuliefern.

Haben wir z. B. eine Datenbanktabelle namens personen mit folgendem Inhalt:

name alter
Maik 32
Heinz Schenk 107

und führen darauf die SQL-Anweisung

delete from personen where alter < 30

aus, so liefert DB2 diese Meldung:

SQL0100W  No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table. SQLSTATE=02000

Das ist ärgerlich, denn wenn ich eine delete-Anweisung ausführe, so möchte ich in erster Linie, dass bestimmte Zeilen in der Tabelle nicht mehr vorkommen. Ob vorher entsprechende Zeilen in der Tabelle existierten oder nicht, ist mir zumeist egal. Die Programmierer von IBM hingegen nehmen offensichtlich an, dass es mir um den Vorgang des Löschens an sich geht.

Bei der interaktiven Arbeit mit der Datenbank ist das nur störend, aber wenn ich mittels einer Programmiersprache auf die Datenbank zugreife, muss ich mir über die Behandlung von Fehlern schon Gedanken machen.

Normalerweise prüfe ich bei einem Datenbankzugriff nur, ob alles reibungslos funktioniert hat. Ist das nicht der Fall, so reiche ich entsprechende Meldungen in Form einer Ausnahme weiter nach oben durchs Programm. Das Verhalten von DB2 zwingt mich aber dazu, den Fehler näher zu untersuchen, denn vielleicht war es ja gar keiner. Dies führt zu unnötiger Komplexität.

Ein vernünftiger Schnittstellenentwurf liefert die Anzahl der effektiv gelöschten Zeilen zurück und erzeugt im Fehlerfall eine Ausnahme. Liefert die delete-Anweisung als Ergebnis 0 zurück, weiß der Entwickler, dass keine Zeilen gelöscht wurden und er kann diese Information nutzen oder es lassen.

Analoges Verhalten zieht sich durch weite Teile der Softwareentwicklung, wie z. B. den Entwurf von Funktionsschnittstellen. Eine Funktion, die ein Feld von Werten zurückliefert, sollte ein leeres Feld und nicht null liefern, wenn nichts gefunden wurde.

Nehmen wir die folgende Java-Klasse:

public class Regal {
    public static Buch[] liesBuecher() {
        if (regal.leer()) return null;
        ...
    }
}

Offensichtlich liefert die Funktion liesBuecher eine Liste von Büchern bzw. null, wenn keine Bücher mehr im Regal stehen.

Wird diese Funktion nun in einem beliebigen Kontext verwendet, so muss der Sonderfall null immer separat geprüft werden:

Buch[] buecher = Regal.liesBuecher();

if (buecher != null)
    for (int i = 0; i < buecher.length; i++)
        if (buecher[i].autor.equals("King"))
            System.out.println("Lese ich!!");

Das ist völlig unnötig und mit großer Wahrscheinlichkeit wird die Prüfung auf null irgendwo vergessen, sodass buecher.length eine NullPointerException auslöst. Dies kann zu sehr subtilen Fehlern führen, die erst sehr spät im Lebenszyklus einer Software auftreten.

Eine bessere Version der Funktion liefert ein leeres Feld zurück, wenn keine Bücher mehr im Regal stehen:

public class Regal {
    public static Buch[] liesBuecher() {
      if (regal.leer()) return new Buch[0];
      ...
    }
}

Damit wird der hässliche Sonderfall eliminiert, ohne Funktionalität einzubüßen:

Buch[] buecher = Regal.liesBuecher();

for (int i = 0; i < buecher.length; i++)
    if (buecher[i].autor.equals("King"))
        System.out.println("Lese ich!!");

Mit

if (buecher.length == 0)

kann man im Bedarfsfall prüfen, ob das Ergebnis leer ist, aber es ist jetzt immer gefahrlos möglich, einen Iterator auf die Bücherliste anzuwenden.

Selbstverständlich steht Java hier stellvertretend für alle Programmiersprachen und sogar für Beschreibungssprachen, denn auch in XML u. ä. sollten leere Listen als solche dargestellt werden und nicht etwa durch ein entsprechendes Fehlerelement. Angenommen, wir erwarten ein XML-Dokument mit folgender Struktur:

<buecher-liste>
  <buch titel="Schatten des Windes"/>
  <buch titel="Wolfsmond"/>
</buecher-liste>

Dann ist es im Falle einer leeren Bücherliste aus den o. g. Gründen um Längen handlicher, das leere Element

<buecher-liste/>

zurück zu bekommen, als z. B.

<fehler msg="Es wurden keine Bücher gefunden!"/>

Share

comments powered by Disqus