Facebook
Twitter
Google+
Kommentare
15

PHP Entwurfsmuster: Composite

Wenn ich mal in Fahrt bin, dann aber richtig. Nachdem wir und gestern das Entwurfsmuster Fassade ein wenig angeschaut haben, ist heute schon das nächste dran. Das Composite Pattern. Das schönste und einfachste Beispiel für dieses Muster ist, so finde ich zumindest, die Logger Klasse. Viele Systeme erlauben es genau einen Logger zu registrieren. Oft reicht dies aber nicht. Man würde gerne mehrere Logger auf einmal verwenden. Das lässt das System aber nicht zu.

Das heute im Fokus stehende Pattern erlaubt es eine Gruppe von Objekten so zu behandeln, als wäre es ein Einzelnes.  Gehen wir aber am besten wieder mit einem Beispiel an die Sache ran:

interface iLogger
{
  public function log( $message );
}

class SuperDuperFramework
{
  private $logger;
  // ...

  public function registerLogger( iLogger $logger )
  {
    $this->logger = $logger;
  }

  public function doSthLogWorthy( )
  {
     $this->logger->log( 'Yihaaaaa' );
  }

So dummerweise kann ich genau einen Logger an die Klasse übergeben. Auch wenn ich gerne mehrere registrieren würde, geht es nicht. Was aber machen? Ganz einfach: Das Kompositum Muster (klingt schrecklich der deutsche Name, oder?) anwenden. Wir brauchen also eine Klasse, die es uns ermöglicht diese Eindimensionalität zu durchbrechen.

class LoggerComposite implements iLogger
{
  private $loggers = array( );

  public function addLogger( iLogger $logger )
  {
    $this->loggers[] = $logger;
  }

  public function log( $message )
  {
    foreach( $this->loggers as $logger ) {
      $logger->log( $message );
    }
  }
}

Eigentlich ganz einfach, was wir hier machen. Wir nehmen uns eine Klasse, die mit mehreren Loggern zurecht kommt, aber nach außen alle Eigenschaften eines Loggers hat, so das wir ihn überall auch als solchen verwenden können. Diesem Logger können wir jetzt ohne Probleme alle unsere Lieblingslogger übergeben und die werden dann nacheinander aufgerufen. Natürlich getriggert von unserem SuperDuperFramework.

Was wieder sehr schön an der Sache ist, dass wir das Framework nicht umgebaut haben. Es weiß also gar nicht, dass sich was geändert hat. So halten wir diese Multilogger-Komplexität vom Framework fern und können sie trotzdem nutzen.

Falls ihr selbst eine Klasse habt, die ihr mit einer Loggerfunktionalität bestücken wollt, dann könnt ihr euch ja überlegen, ob ihr es so lösen wollt oder doch die Multilogger-Fähigkeit gleich in die Klasse mit einbaut. Ich finde in unserem Fall die Composite Lösung elegangter, ist aber nur ein Bauchgefühl. Ich bin mir aber sicher, dass wir da gleich drüber diskutieren und ich am Ende des Tages eine Meinung haben werde.

Ü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

15 Comments

  1. In dem Fall hätte ich einen Dekorierer verwendet, aber das Kompositum funktioniert natürlich auch. In den bisherigen Beispielen, die ich gesehen habe, ging es meistens um Baumstrukturen, die mit dem Kompositum abstrahiert wurden, weshalb das o.g. Beispiel nicht ganz so interessant ist. Ich hätte also gerne mehr über andere Einsatzzwecke gelesen. :-)
    Alles in Allem muss ich jedoch sagen, dass das Kompositum eigentlich nur eine Anwendung von wirklich grundlegenden OOP-Prinzipien darstellt.

    Reply
  2. Ich denke das Composite ist eines der am öftesten unbewusst genutzten Pattern, wenn man objektorientiert arbeitet. Im Grunde ist es ja offensichtlich, das so zu lösen, auch ohne das Muster zu kennen.

    Reply
  3. @Andre: Ganz kurz noch mal zu deiner Dekorierer Lösung. Um einen Decorator zu benutzen, muss ich ja immer eine Grundklasse besitzen. Wäre das bei dir ein Dummy-Logger oder wie würdest du das klären?

    Reply
  4. Ich würde es als abstrakte Klasse lösen, von der die restlichen Logger ableiten. Diese Klasse sollte dann natürlich die Methoden für einen Dekorierer (z.B. decorate(Logger $logger)) bieten. Ein Interface wäre dann nur mehr für eine schönere API nötig.

    Reply
  5. Also ich würde in dem Falle auch das Composite Pattern dem Decorator Pattern vorziehen. Ich füge hier ja im Prinzip keiner Klasse neue Funktionalität zu sondern komponiere mehrere verschiedene „Logging-Aufgaben“ zusammen, die allesamt ausgeführt werden sollen.

    Reply
  6. Ach ja, ein nettes Beispiel, was mir dazu auch noch einfällt wäre das Command-Pattern, das auch gerne mal in Verbindung mit dem Composite-Pattern benutzt wird.

    Reply
  7. Wieder ein sehr guter Artikel von Nils :-)

    Grundsaetzlich ist der gezeigte Einsatz richtig, allerdings hat das Composite noch mehr zu bieten: die Idee ist (wie schon von Andre erwaehnt) Elemente in einer Baumstruktur anzuordnen, um dann Aktionen ueber die Hierarchie weiter zu leiten. Ein (flaches) Web-Beispiel ist die Validierung in einem Formular (siehe zb Zend_Form).
    Oder … ein social network; man fragt ein Person-Objekt nach der Anzahl seiner Bekannten (=Person-Objekte) bis zum n-ten Grad: die Anfrage wird an alle Bekannten weitergeleitet (mit (n-1)), und die Antworten summiert (das Problem von Kreisen im Graph sollte man dabei beruecksichtigen)..

    Reply
  8. @andre: Würde mich da dann auch eher dem Composite widmen, da meine Klassen so gar nicht wissen müssen, dass es noch andere Logger gibt. Aber so ganz verstanden habe ich den Dekorierer Ansatz von dir auch noch nicht :)

    Reply
  9. Hab hier auch zuerst an das Decorater Pattern denken müssen. Ein Logger kann optional via Konstruktor einen anderen Logger aufnehmen, der dann implizit aufgerufen wird. Aber irgendwie ist der Composite Ansatz schon irgendwie sauberer.

    Solche Einträge liebe ich und deshalb schätze ich phphatesme auch. Kurze, knackige Einträge wo man etwas lernt oder zum Nachdenken angeregt wird. Unbedingt weiter so!!!

    Viele Grüße
    Tobi

    Reply
  10. Ein weiteres Beispiel (übrigens aus der Java Welt):
    Bei uns im firmeneigenen Framework kann man für einige Prozesse jeweils eine! Callback Klasse über die Konfiguration angeben um kundenspezifische Anpassungen hinzuzufügen. Mittlerweile sind auf diesem Wege einige Features hinzugekommen, die zu spezifisch sind um ins Framework zu gelangen, aber doch oft genug verwendet werden damit sie ausgelagert werden sollten. Daher plane ich in dafür eine Callback Klasse zu schreiben, über die mehrere „Feature-Callbacks“ verwendet werden können.
    Dass das ganze Composite Pattern heißt, war mit aber bisher nicht bewusst. :)

    Reply
  11. @Rudwig: Das klingt (beim ersten Lesen) nach dem Strategie-Entwurfsmuster.
    @Nils: Ich weiß nicht, weshalb ich den Dekorierer vorziehen würde – wahrscheinlich, wie so häufig, Geschmackssache. Mir ist aufgefallen, dass meine Behauptung, es würde sich nicht um Baumstrukturen handeln, falsch war. Denn mit dem Kompositum kann man auch in deinem Beispiel eine Baumstruktur aufbauen, wobei die inneren Knoten Instanzen des Kompositums sind und die Blätter die Loggerobjekte.
    @Tobi: Ich muss mich dir mittlerweile anschließen was den Einsatz von Dekorierer und Kompositum betrifft.

    Reply
  12. @Andre: Und genau aus diesem Grund ist Pair Programming so was feines. Zwei Ideen und zum Schluss gewinnt die für dieses Problem die beste.
    Ein Ex-Arbeitskollege hatte mir mal erzählt, dass bei ihnen bei der Arbeit ein Zimmer existiert, in dem nur ein großes Plüschtier stand. Dem konnte man zuerst sein Problem erzählen, bevor man damit zu jemanden anderen ging. Finde ich ganz „süß“ den Ansatz. Und hat ja auch was von Pair Programming, aber halt mit ‚nem riesen Teddybären.

    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