Facebook
Twitter
Google+
Kommentare
12

PHP-Entwurfsmuster: Null Object

So heute sind wir mal wieder am Entwurfsmustern. Also wirklich was, das ihr beim Programmieren einsetzen könnt und nicht nur eine Lebensweisheit vom Nils. Wie immer fangen wir mit einem Problem an, dass ich durch die Verwendung des Musters lösen kann.

Nehmen wir also an, wir haben einen Logger, den wir an ein Objekt ranhängen können. Wenn ich aber nichts mitloggen will, dann muss das Objekt aber trotzdem funktionieren. Natürlich fangen wir mit einer recht einfachen, aber auch doofen Lösung an.

class Object
{
  public function doSth( )
  {
     // do sth. very special
     if (!is_null( $this->logger ) {
       $this->logger->log( 'done sth. very special' );
    }
  }
}

Das würde jetzt ohne Probleme funktionieren. Ich will mich aber in meinem Objekt vielleicht gar nicht drum kümmern müssen, ob da jetzt ein Logger initiiert wurde oder nicht. Hier kommt das Null Object in Spiel. Wir implementieren das Null Objekt immer gegen ein Interface. In unsere Fall wäre es das Logger Interface und könnte so aussehen.

class NullLogger implements Logger
{
  public function log( $text )
  {
  }
}

Wenn ihr jetzt diesen Logger in mein Objekt injiziere, so kann ich loggen was ich will, es passiert nichts. Genau so wie ich das gerne hätte. Es ist vielleicht nicht das weltbewegenste Entwurfsmuster, aber es kann Komplexität verringern und das ist ja eines der Hauptaufgaben von schöner Programmierung. Unser fertiges Objekt könnte dann so aussehen:

class Object
{
  public function __construct( Logger $logger )
  {
    $this->logger = $logger;
  }
  public function doSth( )
  {
     // do sth. very special
    $this->logger->log( 'done sth. very special' );
  }
}

Man könnte jetzt natürlich noch einen Standard Logger initialisieren, wenn keine mitgegeben wird, aber das ist Geschmackssache und kommt wohl auf das Objekt an. Wahrscheinlich verwenden viel von euch das Muster ja schon, vielleicht kannten aber ein paar den Namen noch nicht.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

12 Comments

  1. Man könnte auch sagen, du hast ein Dummy-Objekte oder ein Skeleton. Da ist ansich nichts verkehrtes dran. Das hätten andere Entwickler auch so gemacht. Nur: zum „Entwurfsmuster“ würde ich diese Idee nun wirklich nicht adeln.

    Ein Logger ist außerdem ein schlechtes Beispiel. Normalerweise würde man bei einem Logger nur die Funktion welche den Ausgabestrom schreibt überschreiben, wie es AFAIK auch Log4PHP bzw. Log4J machen, aber nicht eine eigene Dummy-Klasse erstellen.

    Reply
  2. @Tom: In der Literatur findest du es als Entwurfsmuster wieder, deswegen bleibe ich erstmal bei der Benamung. Was du mit „der Funktion die den Ausgabestrom schreibt überschreibt“ meinst, weiß ich leider nicht, da kannst du gerne etwas ausholen. So wie ich das verstehe, macht es nämlich nicht immer Sinn, deswegen frage ich lieber noch mal nach.

    Reply
  3. Nils hat da schon Recht: Er selbst hat da garnichts geadelt, es ist faktisch als Entwurfsmuster bekannt. Ob das auch noch von der GoF kommt, oder erst später, weiß ich auch nicht.

    * „Ein Logger ist außerdem ein schlechtes Beispiel. Normalerweise würde man bei einem Logger nur die Funktion welche den Ausgabestrom schreibt überschreiben, wie es AFAIK auch Log4PHP bzw. Log4J machen, aber nicht eine eigene Dummy-Klasse erstellen.“

    Ausgehend vom Interface ist keine weitere Klasse bekannt. Man kommt als (zumindest wenn man logisch bleibt ;)) nicht darum herum eine eigene Dummy-Klasse zu erstellen.

    Reply
  4. Witzig gerade gestern hatte ich dieses Thema hier im Buero aufgeworfen. Mein Bauch sagte mir, dass dieses Entwurfsmuster vielleicht mit zu den am schwierigsten zu erreichenden ist.
    Mal abgesehen von einfachen Beispielen wie dem Logger, der so sinnvollerweise in vielen Projekten implementiert ist, gibt es eine Menge Methoden, die Objekte zurueckgeben oder eben null.
    Wollte man diese alle so refaktorisieren, dass der sie aufrufende Client Code keine null Behandlung vornehmen muss (und darum geht es ja: Client Code soll agnostisch sein), dann muss man ggf. groessere Anderungen an seiner Architektur einplanen.
    Denn oft wird auf den so abgerufenen Objekten ja irgendeine Logik ausgefuehrt. Ist diese Funktionalitaet nicht sauber gekapselt (Separation of Concerns), kann es zu Fehlverhalten fuehren, wenn das Objekt auf dem gearbeitet wird, nicht die Erwartungen erfuellt. Der Client Code will ja wissen, ob seine Berechnungen und Operationen erfolgreich waren und muss entsprechend auf Misserfolg reagieren, was der vermiedenen null Behandlung recht nahe kaeme. Deswegen ist es wichtig, solche Operationen nur dort vorzunehmen, wo ihr Ergebnis auch beurteilt und mit einer korrekten Reaktion versehen werden kann.
    In einem MVC Pattern ergibt sich daraus imho die Regel, dass Controller moeglichst flach sein sollten und keine Businesslogik enthalten ausser auf einer sehr abstrakten Steuerungsebene aehnlich dem Chain of Command Pattern.

    Reply
  5. Nur zur Info: Es ist kein Pattern der GoF. In der englischsprachigen Wikipedia steht aber auch noch, dass es eigentlich nur eine spezielle Form des Strategy-Pattern ist.

    Reply
  6. @KingCrunch: gehört nicht zu den GoF, eben nachgesehen

    Nichtsdestotrotz ist es ein DesignPattern, auch wenn es so simpel ist, es spricht ja nichts dagegen. Das „Template Method“-Pattern ist auch simpel und würde jeder ganz natürlich verwenden, ohne daran zu denken, dass es ein Pattern ist.

    Reply
  7. Oje, nach dem Mittagessen hatte meine Rechtschreibung etwas gelitten. Sorry dafür!

    Jedenfalls ist hier ein Codebeispiel:

    require_once dirname(‚Logger.php‘;
    Logger::configure(‚appender_null.properties‘);
    $logger = Logger::getRootLogger();
    $logger->fatal(„Hello World!“);

    Konfiguration:

    log4php.appender.default = LoggerAppenderNull
    log4php.rootLogger = DEBUG, default

    Der Quellcode der Appender-Klasse sieht wie folgt aus:

    class LoggerAppenderNull extends LoggerAppender {

    /* diverse Funktionen */

    /**
    * Do nothing.
    *
    * @param LoggerLoggingEvent $event
    */
    public function append(LoggerLoggingEvent $event) {
    }
    }

    Wie man sieht: keine Dummy-Klasse, sondern es wurde lediglich die entscheidende Methode gezielt überschrieben.

    Reply
  8. @Daniela: Das ist gar nicht so schwer 🙂 Es wird einfacher zu testen, weil du keine Abhängigkeiten hast. Das Null Objekt sollte so gut wie keine besitzen. Eine andere Implementierung hat bestimmt mehr und muss auch korrekt initialisiert werden. Das doctrine Dingens kenne ich übrigens nicht, ist also reine Spekulation 😉

    Reply
  9. @Tom: Bei dem von dir genannten LoggerAppenderNull handelt es sich doch ebenfalls um das NullObject-Pattern? Es ist nur etwas anders umgesetzt – statt ein Interface zu implementieren, wird von einer abstrakten Klasse abgeleitet. Das Prinzip ist aber das Gleiche, es wird eine Klasse definiert, deren Objekte „nichts“ tun.

    Deswegen trägt die Klasse auch den Bestandteil „Null“ im Namen. 😉

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen