© Liashko/Shutterstock.com
Entwickler Magazin
Kolumne: Die Golumne

Kolumne: Die Golumne

Keiner mag Fehler - weder der Entwickler noch der Nutzer einer Software. Und so strengen wir Entwickler uns intensiv an, sie durch gutes Design, sorgfältige Entwicklung und umfangreiches Testen zu vermeiden oder zumindest frühzeitig zu finden. Dennoch gibt es immer wieder Fehler, die sich gerade während der Laufzeit nicht vermeiden lassen: Dateien, die zur Software gehören und eigentlich immer da sind, werden versehentlich gelöscht. Der Hauptspeicher wird durch andere Anwendungen aufgebraucht. Netzwerkverbindungen werden abgebrochen oder weisen viel höhere Latenzen als gewöhnlich auf. Externe Anwendungen, mit denen kommuniziert wird, oder Datenbanksysteme fallen aus. Diese Liste könnte ich noch ewig fortsetzen, zu umfangreich ist der Dschungel möglicher Fehlerquellen.

Frank Müller


Doch wie soll man mit diesen Fehlern umgehen? Verschiedene Sprachen verfolgen hierzu unterschiedliche Ansätze. Sehr üblich sind heutzutage sogenannte Exceptions. Der Name sagt es bereits: Sie werden als Ausnahmen betrachtet. Als etwas, das nicht sein darf, nicht sein kann. Aber auch hier gibt es unterschiedliche Strategien:Einerseits Laufzeitfehler, die nach dem Motto „Es wird sich schon jemand darum kümmern“ geworfen werden. Andererseits eben Exceptions, die in einigen statischen Sprachen eine sie werfende Funktion bzw. Methode zumindest deklarieren müssen. Ein Aufrufer, der sich nicht um diesen Ausnahmefehler kümmern möchte, muss ihn im Falle einer Exception dann ebenfalls deklarieren.Dieser Weg schafft etwas Transparenz in der Fehlerbehandlung, verführt aber auch zu zwei möglichen Wegen: Einerseits werden von einer Funktion, die andere mit Exceptions nutzen, diese nur weitergereicht. In diesem Fall wird der Call Stack bis zur Ursache immer tiefer. Andererseits werden größere Blöcke in ein try/catch geklammert und die anschließende Behandlung der auslösenden Exception fällt schwer. Variablen sind teilweise nicht im Scope oder es ist kompliziert, den Verursacher zu identifizieren. Es werden bspw. Daten aus dem Netz in die Dateien A und B geschrieben. Während bei A alles fehlerfrei verläuft, löst B eine IOException aus. Nun gilt es, A noch sauber zu schließen, oder gar ein sauberes Rollback (im Falle einer Datenbank) oder eine entsprechende Meldung (bei einem Netzwerkempfänger) durchzuführen. Hier zeigen sich einige Probleme, die bei undifferenzierter Behandlung auftreten können.Fehler in der Welt von GoAnsonsten ist es jedoch Konvention, den Fehlerwert als einzigen oder letzten Wert zurückzugeben, bspw. Foo() error oder Bar() (string, error). Liegt kein Fehler vor, ist dieser Rückgabewert einfach nil. Gibt es mehrere Rückgabewerte, ist es Konvention, entweder gültige Werte und dazu den Fehlerwert nil oder alle Werte als Nullwerte sowie den korrekten Fehler als Ergebnis zu liefern. Rückgabewerte zusammen mit einem Fehler sind zu vermeiden.Nehmen wir als Beispiel eine ganzzahlige Division. Sie soll entweder das korrekte Ergebnis oder einen Fehler zurückgeben. Hierzu ist es notwendig, beide Typen in der Rückgabe zu haben. Gleichzeitig wird für die Erzeugung eines Fehlers das Package errors importiert:func Divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("cannot divide by zero") } return a/b, nil}Listing 1// Variant A.d, err := Divide...

Entwickler Magazin
Kolumne: Die Golumne

Kolumne: Die Golumne

Keiner mag Fehler - weder der Entwickler noch der Nutzer einer Software. Und so strengen wir Entwickler uns intensiv an, sie durch gutes Design, sorgfältige Entwicklung und umfangreiches Testen zu vermeiden oder zumindest frühzeitig zu finden. Dennoch gibt es immer wieder Fehler, die sich gerade während der Laufzeit nicht vermeiden lassen: Dateien, die zur Software gehören und eigentlich immer da sind, werden versehentlich gelöscht. Der Hauptspeicher wird durch andere Anwendungen aufgebraucht. Netzwerkverbindungen werden abgebrochen oder weisen viel höhere Latenzen als gewöhnlich auf. Externe Anwendungen, mit denen kommuniziert wird, oder Datenbanksysteme fallen aus. Diese Liste könnte ich noch ewig fortsetzen, zu umfangreich ist der Dschungel möglicher Fehlerquellen.

Frank Müller


Doch wie soll man mit diesen Fehlern umgehen? Verschiedene Sprachen verfolgen hierzu unterschiedliche Ansätze. Sehr üblich sind heutzutage sogenannte Exceptions. Der Name sagt es bereits: Sie werden als Ausnahmen betrachtet. Als etwas, das nicht sein darf, nicht sein kann. Aber auch hier gibt es unterschiedliche Strategien:Einerseits Laufzeitfehler, die nach dem Motto „Es wird sich schon jemand darum kümmern“ geworfen werden. Andererseits eben Exceptions, die in einigen statischen Sprachen eine sie werfende Funktion bzw. Methode zumindest deklarieren müssen. Ein Aufrufer, der sich nicht um diesen Ausnahmefehler kümmern möchte, muss ihn im Falle einer Exception dann ebenfalls deklarieren.Dieser Weg schafft etwas Transparenz in der Fehlerbehandlung, verführt aber auch zu zwei möglichen Wegen: Einerseits werden von einer Funktion, die andere mit Exceptions nutzen, diese nur weitergereicht. In diesem Fall wird der Call Stack bis zur Ursache immer tiefer. Andererseits werden größere Blöcke in ein try/catch geklammert und die anschließende Behandlung der auslösenden Exception fällt schwer. Variablen sind teilweise nicht im Scope oder es ist kompliziert, den Verursacher zu identifizieren. Es werden bspw. Daten aus dem Netz in die Dateien A und B geschrieben. Während bei A alles fehlerfrei verläuft, löst B eine IOException aus. Nun gilt es, A noch sauber zu schließen, oder gar ein sauberes Rollback (im Falle einer Datenbank) oder eine entsprechende Meldung (bei einem Netzwerkempfänger) durchzuführen. Hier zeigen sich einige Probleme, die bei undifferenzierter Behandlung auftreten können.Fehler in der Welt von GoAnsonsten ist es jedoch Konvention, den Fehlerwert als einzigen oder letzten Wert zurückzugeben, bspw. Foo() error oder Bar() (string, error). Liegt kein Fehler vor, ist dieser Rückgabewert einfach nil. Gibt es mehrere Rückgabewerte, ist es Konvention, entweder gültige Werte und dazu den Fehlerwert nil oder alle Werte als Nullwerte sowie den korrekten Fehler als Ergebnis zu liefern. Rückgabewerte zusammen mit einem Fehler sind zu vermeiden.Nehmen wir als Beispiel eine ganzzahlige Division. Sie soll entweder das korrekte Ergebnis oder einen Fehler zurückgeben. Hierzu ist es notwendig, beide Typen in der Rückgabe zu haben. Gleichzeitig wird für die Erzeugung eines Fehlers das Package errors importiert:func Divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("cannot divide by zero") } return a/b, nil}Listing 1// Variant A.d, err := Divide...

Neugierig geworden?


   
Loading...

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang