Einführung in das Nullobjektmuster

1. Übersicht

In diesem kurzen Tutorial sehen wir uns das Null-Objektmuster an, einen Sonderfall des Strategiemusters. Wir werden seinen Zweck beschreiben und wann wir tatsächlich darüber nachdenken sollten, ihn zu verwenden.

Wie üblich werden wir auch ein einfaches Codebeispiel bereitstellen.

2. Null-Objektmuster

In den meisten objektorientierten Programmiersprachen, wir sind nicht erlaubt , eine verwenden null Referenz. Deshalb sind wir oft gezwungen, Nullprüfungen zu schreiben :

Command cmd = getCommand(); if (cmd != null) { cmd.execute(); }

Wenn die Anzahl solcher if- Anweisungen hoch wird, kann der Code manchmal hässlich, schwer lesbar und fehleranfällig werden. In diesem Fall kann das Null-Objektmuster nützlich sein.

Mit dem Nullobjektmuster soll diese Art der Nullprüfung minimiert werden . Stattdessen können wir das Nullverhalten identifizieren und in den vom Clientcode erwarteten Typ einkapseln. Meistens ist eine solche neutrale Logik sehr einfach - tun Sie nichts. Auf diese Weise müssen wir uns nicht mehr mit der speziellen Behandlung von Nullreferenzen befassen .

Wir können Nullobjekte einfach genauso behandeln wie jede andere Instanz eines bestimmten Typs, die tatsächlich eine komplexere Geschäftslogik enthält. Folglich bleibt der Client-Code sauberer.

Da Nullobjekte keinen Status haben sollten, müssen identische Instanzen nicht mehrmals erstellt werden. Daher implementieren wir häufig Nullobjekte als Singletons .

3. Das UML-Diagramm des Null-Objektmusters

Schauen wir uns das Muster visuell an:

Wie wir sehen können, können wir die folgenden Teilnehmer identifizieren:

  • Der Client benötigt eine Instanz von AbstractObject
  • AbstractObject definiert den Vertrag, den der Client erwartet - er kann auch eine gemeinsame Logik für die implementierenden Klassen enthalten
  • RealObject implementiert AbstractObject und bietet reales Verhalten
  • NullObject implementiert AbstractObject und bietet neutrales Verhalten

4. Implementierung

Nachdem wir eine klare Vorstellung von der Theorie haben, schauen wir uns ein Beispiel an.

Stellen Sie sich vor, wir haben eine Nachrichtenrouteranwendung. Jeder Nachricht sollte eine gültige Priorität zugewiesen sein. Unser System soll Nachrichten mit hoher Priorität an ein SMS-Gateway weiterleiten, während Nachrichten mit mittlerer Priorität an eine JMS-Warteschlange weitergeleitet werden sollen.

Von Zeit zu Zeit können jedoch Nachrichten mit "undefinierter" oder leerer Priorität in unsere Anwendung gelangen. Solche Nachrichten sollten von der weiteren Verarbeitung ausgeschlossen werden.

Zuerst erstellen wir die Router- Schnittstelle:

public interface Router { void route(Message msg); }

Als Nächstes erstellen wir zwei Implementierungen der obigen Schnittstelle - diejenige, die für das Routing zu einem SMS-Gateway verantwortlich ist, und diejenige, die die Nachrichten an die JMS-Warteschlange weiterleitet:

public class SmsRouter implements Router { @Override public void route(Message msg) { // implementation details } }
public class JmsRouter implements Router { @Override public void route(Message msg) { // implementation details } }

Zum Schluss implementieren wir unser Null-Objekt:

public class NullRouter implements Router { @Override public void route(Message msg) { // do nothing } }

Wir sind jetzt bereit, alle Teile zusammenzufügen. Mal sehen, wie der Beispiel-Clientcode aussehen könnte:

public class RoutingHandler { public void handle(Iterable messages) { for (Message msg : messages) { Router router = RouterFactory.getRouterForMessage(msg); router.route(msg); } } }

Wie wir sehen können, behandeln wir alle Router- Objekte gleich, unabhängig davon, welche Implementierung von der RouterFactory zurückgegeben wird. Dadurch können wir unseren Code sauber und lesbar halten.

5. Wann wird das Nullobjektmuster verwendet?

Wir sollten das Null-Objektmuster verwenden, wenn ein Client andernfalls nach Null suchen würde , um die Ausführung zu überspringen oder eine Standardaktion auszuführen. In solchen Fällen können wir die neutrale Logik in ein Nullobjekt einkapseln und diese anstelle des Nullwerts an den Client zurückgeben. Auf diese Weise muss der Client-Code nicht mehr wissen, ob eine bestimmte Instanz null ist oder nicht.

Ein solcher Ansatz folgt allgemeinen objektorientierten Prinzipien wie Tell-Don't-Ask.

Um besser zu verstehen, wann wir das Null-Objektmuster verwenden sollten, stellen wir uns vor, wir müssen die CustomerDao- Schnittstelle implementieren , die wie folgt definiert ist:

public interface CustomerDao { Collection findByNameAndLastname(String name, String lastname); Customer getById(Long id); }

Die meisten Entwickler würden zurückkehren Collections.emptyList () von findByNameAndLastname () , falls keiner der Kunden entspricht die bereitgestellten Suchkriterien. Dies ist ein sehr gutes Beispiel für das Befolgen des Nullobjektmusters.

Im Gegensatz dazu sollte get ById () den Kunden mit der angegebenen ID zurückgeben. Jemand, der diese Methode aufruft, erwartet, dass er die spezifische Kundenentität erhält. Falls kein solcher Kunde existiert, sollten wir explizit null zurückgeben, um zu signalisieren, dass etwas mit der angegebenen ID nicht stimmt.

Wie bei allen anderen Mustern müssen wir unseren spezifischen Anwendungsfall berücksichtigen, bevor wir das Null-Objektmuster blind implementieren . Andernfalls können wir unbeabsichtigt einige Fehler in unseren Code einfügen, die schwer zu finden sind.

6. Fazit

In diesem Artikel haben wir erfahren, was das Nullobjektmuster ist und wann wir es verwenden dürfen. Wir haben auch ein einfaches Beispiel für das Entwurfsmuster implementiert.

Wie üblich sind alle Codebeispiele auf GitHub verfügbar.