Facebook
Twitter
Google+
Kommentare
7

PHP Entwurfsmuster: Decorator

In meinem allerersten Posting hatte ich ja bereits groß rum getönt, dass ich auf Themen aus der Softwaretechnik eingehen will. Heute ist es endlich soweit, denn mir ist ein schönes Beispiel für das Entwurfsmuster Dekorierer eingefallen. Bei der Definition habe ich einfach mal den Text aus der Wikipedia Vgenommen.

Der Decorator (auch Dekorierer) ist ein Entwurfsmuster aus dem Bereich der Softwareentwicklung und gehört zur Kategorie der Strukturmuster (Structural Patterns). Das Muster ist eine flexible Alternative zur Unterklassenbildung, um eine Klasse um zusätzliche Funktionalitäten zu erweitern. Es ist ein Entwurfsmuster der sogenannten GoF-Muster (siehe Viererbande).

Wir verwenden das Muster also um Klassen zu erweitern. Aber warum kann man dies nicht einfach über Vererbung bewerkstelligen? Ganz einfach, manchmal will man auch eine Klasse zur Laufzeit erweitern und in diesem Fall kann dieses Pattern helfen. Ein weiterer Vorteil wird einem durch eine einfache Problemstellung klar, die man nicht einfach durch Vererbung lösen kann.

Stellen Sie sich vor, Sie müssen ein Auto als Objekt abbilden – ja ich weiß, die doofen Auto Beispiele, aber später werde ich Probleme vorstellen die aus meinem Arbeitsumfeld kommen. Dieses Auto soll also um zwei Features erweitert werden, das erste Feature ist ein Spoiler, der das Auto um 10 km/h schneller macht. Das zweite sind Bremsscheiben, die den maximale Bremsweg halbieren. Jetzt benötigen Sie Instanzen von vier Autos.

  1. Ein Auto, das weder Spoiler noch verbesserte Bremsscheiben kennt
  2. Ein Auto, das einen Spoiler besitzt, aber keine neuen Bremsscheiben
  3. Ein Auto, das Bremsscheiben besitzt, aber keinen Spoiler
  4. Ein Auto, das Bremsscheiben und Spoiler besitzt

Klingt eigentlich ganz einfach, aber wenn man sich mal genau Gedanken drüber macht, ist es nicht so trivial, wie es im ersten Augenblick den Eindruck macht. Und leider mussten wir auch bei unseren Assessmentcentern die Erfahrung machen, dass viele Bewerber dieses Problem nicht lösen konnten. Delegation über einen Dekorierer wäre hier das Zauberwort gewesen.

Aber gehen wir doch einfach zu einem Beispiel, dass mir die letzten Tage untergekommen ist. Es ging darum eine einfache Collection zu entwerfen. Diese Sammlung soll einfach als Container für alle möglichen Objekte dienen. Als Vorbild haben wir uns dafür das Collection Interface von Java geschnappt und es nachimplementiert. Ich hab jetzt mal das minimal Set dieses Interfaces angegeben. Es fehlen zwar ein paar Funktionen aber für die Erläuterung dieses Entwurfsmuster sollte es ausreichen.

        interface iCollecton extends Iterator, Countable
        {
          public function add( $element );
          public function remove ( $element );
        }

Gegen dieses Interface sollen nun diverse Klassen implementiert werden. Set, HashMap und List wäre hier nur drei einfache Beispiele. Jetzt mussten diese Container in zwei verschiedenen Varianten existieren. Die erste Variante sollte alle möglichen Elemente entgegennehmen können. Die Zweite sollte typsicher sein, sie sollte nur Elemente von einem bestimmten Typ annehmen, der im Konstruktor definiert wurde. Über Ableitung kann dies leider nicht bewerkstelligt werden, ohne Code zu duplizieren. Über den Dekorierer ist dies aber kein Problem.

          class TypeSaveCollectionDecorator implements iCollection
          {
            private $collection;
            private $type;

            public function __construct( iCollection $collection, $type )
            {
              $this->collcetion = $collection;
              $this->type = $type;
            }

            public function add( $element )
            {
              if ( ! ( $element instanceof $this->type ) ) {
                throw new Exception( 'Wrong type' );
              }
              return $this->collection->add( $element );
            }

            public function remove( $element )
            {
              return $this->collection->remove( $element );
            }
         }

Sieht eigentlich ganz einfach aus. Und das beste daran ist: es ist auch einfach! Wenn man sich mal mit diesem Muster beschäftigt hat, dann will man es nicht wieder missen. Der Aufruf des Decorators ist jetzt auch ganz einfach.

$parkingLot = new TypeSaveCollectionDecorator( new Set( ), 'iCar' );

Vielleicht konnte ja dieser tipp dem einen oder anderen helfen. Wie immer bei Design Pattern Fragen, kann ich nur das Buch der Gang of four empfehlen. Wer speziell an der PHP Implementierung interessiert ist, der sollte sich das Buch von Stefan Schmidt „PHP Design Pattern“ mal genauer ansehen.

Zum Schluss sei noch erwähnt, dass ich der Einfachheit halber alle Funktionen des Countable und der Iterator Interfaces weggelassen habe.

Ü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

7 Comments

  1. was würde christian schmidt nur empfehlen? aber du schreibst es ja am ende des artikels.

    dabei wäre noch drauf hinzuweisen, das es sich um ein standardwerk der php entwicklung handelt und die deutschsprachige ausgabe nächstes jahr bei o’reilly in die zweite runde geht.

    unbedingt lesen, egal ob man mit zend framework oder etwas anderem arbeitet.

    Reply
  2. wo soll der sinn in einem artikel sein, der nur ein problem schildert, zusätzlich auch noch durch den schwank auf ein ganz anderes beispiel zusätzlich verwirrt, muss ja nicht alles verstehen.

    Reply
  3. Hi,

    da ist ein Buchstabendreher im 2. Codebeispiel in Zeile 8:

    $this->collcetion = $collection;

    Das erste „collcetion“ soll sicher auch ein „collection“ sein oder?

    Schöne grüße! und ich liebe diese Seite 😉

    Hannes

    Reply
  4. Hmmm… und was ist nun mit den autos?
    wo ist die erklärung der dekoratoren?
    der beitrag zeigt nur wie ich einen object kontainer typen sicher fülle, nicht aber wie ich das auto tatschächlich dekoriere und ganz wichtig: wie das mit der delegation funktioniert:
    Auto,
    AutoSpoiler
    AutoSpoilerBremsen
    AutoBremsen
    AutoBremsenSpoiler
    es sind 5 und nicht 4 Zustände! 🙂
    hier sind mehr fragen offen als beantwortet. 🙁

    huch: 2008, naja, das pattern ist ja noch älter aber dieser artikel sollte überholt werden!

    Reply
  5. Moin,
    Das Beispiel ist ungenügend. Hier werden die Vorraussetzungen gezeigt aber NICHT wie man dekoriert, sprich: eine Hülle um ein Objekt legt die Funktionalität erweitert und die zu grunde liegende Basis Klasse benutzt. Das fehlt hier.
    Derzeit sehe ich nur eine Klasse die dekorierer genannt wird. Das reicht nicht. :-/
    Der code passt besser zum collection pattern!

    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