Null Object
Aus php bar
Das Null Objekt ist ein Entwurfsmuster aus der Softwareentwicklung. Ein Null Objekt ist eine Implementierung einer Schnittstelle ohne Funktionalität. Somit kann reagiert werden, wenn ein Objekt zum entsprechenden Zeitpunkt noch nicht instanziert wurde und somit ein Null-Object als Stellvertreter ohne Funktion geladen wird. Zudem werden Ausnahmebehandlungen für fehlende Objekte reduziert.
Ausgangssituation
Wir haben eine Klasse SimpleMailer welche die Schnittstelle Mailer implementiert.
1 interface Mailer 2 { 3 function setMessage($text); 4 function setSubject($subject); 5 function setReceiver($receiver); 6 function setFrom($from); 7 function send(); 8 }
Basierend auf der PHP mail Funktion erstellen wir eine Klasse die unsere Mailer Schnittstelle implementiert. Entsprechend unserer Schnittstellen werden nur vier Setter und eine Methode zum versenden implementiert.
1 class SimpleMailer 2 implements Mailer 3 { 4 private $_subject; 5 private $_text; 6 private $_receiver; 7 private $_from; 8 9 function setMessage($text) 10 { 11 $this->_text = $text; 12 } 13 14 function setSubject($subject) 15 { 16 $this->_subject = $subject; 17 } 18 19 function setReceiver($receiver) 20 { 21 $this->_receiver = $receiver; 22 } 23 24 function setFrom($from) 25 { 26 $this->_from = $from; 27 } 28 29 function send() 30 { 31 return mail($this->_receiver, 32 $this->_subject, 33 $this->_text, 34 "From: {$this->_from}" 35 ); 36 } 37 }
Mit der nächsten Klasse wird das Mailer Objekt vorkonfiguriert und bietet eine Methode zum Versenden einer Nachricht an eine bestimmte E-Mail Adresse.
1 class Notifier 2 { 3 private $_mailer; 4 5 function setMailer(Mailer $mailer) 6 { 7 $this->_mailer = $mailer; 8 } 9 10 function notify($userEmail) 11 { 12 $this->_mailer->setSubject("[nachricht] Hallo!"); 13 $this->_mailer->setMessage("Das ist eine Nachricht."); 14 $this->_mailer->setFrom("admin@example.com"); 15 $this->_mailer->setReceiver($userEmail); 16 return $this->_mailer->send(); 17 } 18 }
Die Anwendung ist denkbar einfach:
1 $notifier = new Notifier(); 2 $notifier->setMailer(new SimpleMailer()); 3 $notifier->notify('client@example.com');
Problem
Jetzt besteht aber das Problem, daß wir nicht immer ein funktionales SimpleMailer Objekt verfügbar machen können. Wenn wir keines an unseren Notifier übergeben, würde in der Methode notify ein Fehler auftreten, da auf ein nicht definiertes Objekt zugegriffen wird. Eine mögliche Lösung für dieses Problem könnte so aussehen:
1 class Notifier 2 //... 3 function notify($userEmail) 4 { 5 if (!$this->_mailer) 6 return false; 7 $this->_mailer->setSubject("[nachricht] Hallo!"); 8 $this->_mailer->setMessage("Das ist eine Nachricht."); 9 $this->_mailer->setFrom("admin@example.com"); 10 $this->_mailer->setReceiver($userEmail); 11 return $this->_mailer->send(); 12 } 13 //... 14
Implementierung
Bei komplexeren Anwendungen werden viele If Abfragen aber schnell unübersichtlich. Außerdem könnte es ja sein, daß wir die Notifier Klasse gar nicht verändern wollen oder gar können. Änderungen im vorhandenen Code bringen oft viele Probleme mit sich. Deswegen versuchen wir, so wenige Änderungen wie möglich vorzunehmen. An dieser Stelle definieren wir uns für die Mailer Schnittstelle ein Null Objekt. Dieses beinhaltet leere Setter und eine send Methode, die lediglich false zurück gibt. Das Null Objekt konsumiert alle Zugriffe und führt keine weiteren Aktionen aus.
1 class NullMailer 2 implements Mailer 3 { 4 function setMessage($text) { } 5 function setSubject($subject) { } 6 function setReceiver($receiver) { } 7 function setFrom($from) { } 8 function send() 9 { 10 return false; 11 } 12 }
Jetzt übergeben wir dem Notifier eine Instanz des NullMailer.
1 $notifier = new Notifier(); 2 $notifier->setMailer(new NullMailer()); 3 $notifier->notify('client@example.com');
Um nun den Notifier ohne eine funktionale Mailer Instanz lauffähig zu machen, haben wird nur ein simples Null Objekt erstellt und dieses übergeben. So vermeiden wir über eine elegante Herangehensweise Ausnahmebehandlungen und können auch leicht Funktionalität aus einer Anwendung herausnehmen.
Mit dem Muster haben wir jedoch ein kleines Problem eingeführt. Manchmal ist es einfach notwendig festzutellen, ob wir ein Null Objekt oder ein funktionales Objekt vorliegen haben. In unserem Fall möchten wir wissen, ob wir überhaupt Mails versenden können. Für diesen Fall wird eine weitere Schnittstelle benötigt, die diese Information preisgibt.
1 interface Null 2 { 3 function isNull(); 4 }
Nun erweitern wir die Mailer Schnittstelle mit der neuen Schnittstelle:
1 interface Mailer extends Null 2 { 3 //... 4 }
Und ändern dementsprechend die Mailer Klassen:
1 class SimpleMailer implements Mailer 2 { 3 //... 4 function isNull() 5 { 6 return false; 7 } 8 //... 9 } 10 11 class NullMailer implements Mailer 12 { 13 //... 14 function isNull() 15 { 16 return true; 17 } 18 //... 19 }
Nun erweitern wir den Notifier um eine Methode, die unsere Information liefert.
1 class Notifier 2 { 3 //... 4 function isEnabled() 5 { 6 return !$this->_mailer->isNull(); 7 } 8 //... 9 }
Falls der Notifier nicht verändert werden kann, kann man diese Methode auch über einen Adapter einfügen.
Jetzt haben wir die Möglichkeit abzufragen ob wir Mails versenden können oder nicht, im Code könnte das so aussehen:
1 $notifier = new Notifier(); 2 $notifier->setMailer(new SimpleMailer()); 3 if ($notifier->isEnabled()) 4 $notifier->notify('user@example.com'); 5 else 6 echo "Benachrichtigungssystem ist deaktiviert!";

