Null-Objekt

In diesem Post will ich die Problematik von Nothing beleuchten und was man besser machen kann. Oft gibt es in Code viele Prüfungen auf Nothing. Andauernd könnte es sein, dass ein Objekt nicht gesetzt ist und es deshalb zu einer Exception kommt.   Oft dient die Prüfung nur der Vermeidung einer Exception. Seltener jedoch will man wirklich wissen, ob das Objekt nicht gesetzt ist. Ein bekanntes Muster ist das Null-Objekt. Dieses hilft das Problem zu umgehen.

Anstatt Nothing zu benutzen, wird ein Objekt benutzt, welches ein nicht gesetztes Objekt repräsentiert. Pro Vererbungshierarchie muss man ein Null-Objekt erzeugen. Es gibt aber auch Objekte, die einen Null-Zustand besitzen. Ein Beispiel hierfür ist eine leere Liste. Dies soll auch als erstes betrachtet werden. Betrachtet wird eine Funktion, die Tiere sucht und eine Auflistung (IEnumerable) von Tieren zurückgibt. Wenn kein Tier gefunden wurde, könnte die Funktion Nothing zurück geben. Dann würde jeder Entwickler der die Funktion nutzt das Ergebnis auf Nothing prüfen, um zu verhindern, dass es zu einer Exception kommt. Wird stattdessen aber eine leere Liste zurückgegeben, dann kann diese einfach in einer Schleife durchlaufen werden. Die Schleife wird dann einfach nicht ausgeführt. Der Entwickler kann den leeren Zustand aber auch abfragen, wenn er dies möchte. Im Gegensatz zur ersten Möglichkeit wird er aber nicht dazu gezwungen.

Die zweite Variante des Null-Objektmusters kommt bei der Vererbung/Polymorphie zum Einsatz. Eine Funktion die einen Logger (ILog) zurück gibt, könnte Nothing zurückliefern, weil kein Logger gefunden wurde. Dann müsste, bevor der Logger benutzt wird auf Nothing geprüft werden. Wenn stattdessen ein NullLogger zurückgeliefert wird, der das Interface ILog mit einer leeren Funktion implementiert, dann kann die Rückgabe sorgenfrei verwendet werden. Will der Entwickler wirklich wissen, ob er einen Logger bekommen hat, kann dies Beispielsweise mit instanceof geprüft werden. Oder es kann eine Funktion angeboten werden, die eine solche Prüfung übernimmt.

Ein Nachteil des Musters ist, dass Fehler übersehen werden könnten. Denn wenn keine Exception kommt, könnte der Entwickler denken das alles in Ordnung ist. Um dieses Problem zu mindern, sollte in die Funktion, die leer gelassen wird, wenigstens ein Debug.WriteLine eingefügt werden. Dadurch kann der Entwickler auf den Umstand des Null-Objekts hingewiesen werden.

Im Folgenden ist das Beispiel mit dem ILog-Interface noch einmal aufgeführt:

Public Interface ILog
    Sub Log(message As String)
End Interface

Module LoggerFactory

    Private loggerList As IDictionary(Of String, ILog)

    .....

    Public Function GetLogger(id As String) As ILog
        If loggerList.ContainsKey(id) Then
            Return loggerList.Item(id)
        Else
            Return New NullLogger()
        End If
    End Function
End Module

Public Class NullLogger
    Implements ILog

    Public Sub Log(message As String) Implements ILog.Log
        Debug.WriteLine("Dies ist ein Null-Objekt. Es wurde kein Logger gefunden.")
    End Sub
End Class

Module MyProgramm
    Sub Main()
        Dim logger As ILog = LoggerFactory.GetLogger("MyLogger")
        logger.Log("Ein Eintrag")
        logger.Log("Noch ein Eintrag")
    End Sub
End Module

Zusammenfassend kann man sagen, dass das Null-Objekt Muster helfen kann überflüssige Prüfungen auf Nothing zu vermeiden. Außerdem kommt es zu weniger Exceptions, die aufgrund von vergessenen Prüfungen entstehen können. Die Gefahr Fehler zu übersehen kann mit Hilfe von Debug.WriteLine oder einem Logger gemildert werden.

Advertisements