• PHP Entwurfsmuster: Null Object

    von am 26. Januar 2010
    Dieser Artikel wurde auf Wunsch der phphatesme Leser verfasst und wurde über die Ideenschmiede eingereicht. Falls du auch eine Idee für einen Artikel hast, dann füge sie doch einfach hinzu.

    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.

    Nils Langner

    Auch wenn Ihr es mir nicht glauben werdet, aber ich habe nichts gegen PHP. Ich rege mich einfach nur gerne auf. Ok so schlimm ist es auch nicht. Eigentlich wollte ich schon immer einen Blog haben und da ...

    Zum Profil von Nils Langner

    12 Kommentare »


    • Tom
      am 26. Januar 2010 um 09:30 Uhr

      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.


    • Nils Langner
      am 26. Januar 2010 um 09:37 Uhr

      @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.


    • KingCrunch
      am 26. Januar 2010 um 09:44 Uhr

      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.


    • Christian
      am 26. Januar 2010 um 10:25 Uhr

      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.


    • Cem Derin
      am 26. Januar 2010 um 11:42 Uhr

      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.


    • Tobias Petry
      am 26. Januar 2010 um 11:50 Uhr

      @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.


    • Tom
      am 26. Januar 2010 um 14:03 Uhr

      @Nils was den Logger angeht: dort werden an den (unveränderten) Logger sogenannte “Appender” gebunden. Diese besorgen steuern den Ausgabestrom. (Und natürlich gibt es einen “NullAppender”, der alle Ausgaben nach dev/null leitet.)

      Siehe zum Beiuspiel hier: http://incubator.apache.org/log4php/docs/appenders.html


    • Tom
      am 26. Januar 2010 um 14:11 Uhr

      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.


    • Nils Langner
      am 26. Januar 2010 um 15:46 Uhr

      @Tom: Ist auch ne Möglichkeit, ob es die sinnvollste ist müsste man wohl mal ausdiskutieren. Aber verstanden habe ich jetzt auf jeden Fall, was du meintest.


    • Daniela Waranie
      am 27. Januar 2010 um 13:03 Uhr

      Bei diesem Titel habe ich zu aller erst an das Doctrine Null Object gedacht…:
      http://www.doctrine-project.org/Doctrine_Null/1_0
      und vermutet dass dieses hier mal erklärt zu bekommen…

      Naja vielleicht kann mir da mal jemand weiterhelfen (warum soll das null testen mit diesem Object schneller gehen?). Gibt es hierfür auch ein Pattern-Namen?


    • Nils Langner
      am 27. Januar 2010 um 13:06 Uhr

      @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 ;)


    • Timo Reitz
      am 27. Januar 2010 um 18:18 Uhr

      @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. ;-)

    RSS Feed für Kommentare zu diesem Artikel. TrackBack URL

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.