Das Decorator Design Pattern

Einführung

Das Decorator Design Pattern ermöglicht es neue Methoden zur Laufzeit zu einem Objekt hinzuzufügen. In einem simplen Beispiel möchte ich ihnen die Funktionsweise dieses Pattern zeigen. Zur Umsetzung des Pattern verwende ich die Möglichkeiten von Php 5.4. Für die bildliche Darstellung verwende ich keine Klasse 'Auto'.
Was eigentlich völlig unmöglich ist !

Zur Darstellung des Docorater Pattern verwende ich eine Geburtstagstorte.
Wie immer findet sie das Skript im Framework Frink2 - Dekoration Pattern.

Klasse Torte

Ich erstelle als erstes die Basisklasse 'Torte'. Durch die Erweiterung der Basisklasse mit '\models\ModelData' entsteht ein Standard-Model für das Framework Frink2

class Torte extends \models\modelBasis
{
    /**
     *
     * @return string
     */
    public function zuckerGuss()
    {
        return 'Zuckerguss';
    }

    /**
     * @return string
     */
    public function hasFruechte()
    {
        return 'Fruechte';
    }

    /**
     * @return string
     */
    public function hasKalorien()
    {
        return 'gewaltig Kalorien';
    }
}

erweitern des Standard Model

Diese Basisklasse wird um ein Trait erweitert. Dieser Trait ermöglicht die dynamische Erweiterung unseres Model um weitere Methoden.

class TorteMitDekoration extends \models\Torte
{
    use \traits\addMethods;

}

Trait

Der Trait beinhaltet folgende Methoden.

trait addMethods
{
    private $methods = array();

    public function addMethod($methodName, $methodCallable)
    {
        if (!is_callable($methodCallable)) {
            throw new frinkError('zweite Parameter muss eine aufrufbare Funktion beinhalten', 3);
        }

        // $this->methods[$methodName] = Closure::bind($methodCallable, $this, get_class());
        $this->methods[$methodName] = $methodCallable;
    }

    public function __call($methodName, array $args)
    {
        if (isset($this->methods[$methodName])) {
            return call_user_func_array($this->methods[$methodName], $args);
        }

        throw new frinkError('Diese Methode ist nicht vorhanden');
    }
}

Unser Dekoration Pattern in der praktischen Anwendung:

Im '__construct()' des Controller übergeben wir das Model an den DI Container Pimple.

 public function __construct($controllerName, $actionName)
{
    // Vorbereitung des Pimple DI Container
    $models = [
        'torteMitDekoration' => function ($pimple) {
            return new \models\TorteMitDekoration($pimple);
        }
    ];

    parent::pimple($models);       
}

Im Controller existiert eine Action mit dem Namen 'decorationPattern()'

public function decorationPattern()
{
    try {
        // Dekorieren der Torte
        $this->torteVorbereiten();

        // Geburtstagsfeier
        $this->Geburtstagsfeier();

        $this->template();
    }
        // eigene Exception
    catch (\tools\frinkError $e) {
        throw $e;
    } // Exception anderer Klassen
    catch (\Exception $e) {
        throw $e;
    }
}

Diese Action ruft drei Methoden im Controller auf.
Als erstes wird die Klasse in der Methode 'torteVorbereiten()' aufgerufen.

protected function torteVorbereiten()
{
    // PhpStorm mitteilen welches Model verwendet wird
    /** @var $torteMitDekoration \models\TorteMitDekoration */
    $torteMitDekoration = $this->pimple['torteMitDekoration'];

    // dynamisches hinzufügen von Methoden
    $torteMitDekoration->addMethod('kerzen', function(){
        return 'Kerzen verwenden';
    });

    $torteMitDekoration->addMethod('anzuenden', function(){
        return 'Kerzen anzuenden';
    });

    $torteMitDekoration->addMethod('auspusten', function (){
        return 'Kerzen auspusten';
    });

    // die Torte in den Kühlschrank stellen
    $this->pimple['torteMitDekorationUndKerzen'] = $torteMitDekoration;

    return;
}

In der zweiten Methode 'Geburtstagsfeier()' wird das Dekoration Pattern genutzt.

protected function Geburtstagsfeier()
{
    // Torte aus dem Kühlschrank holen

    /** @var $torteMitDekorationUndKerzen \models\TorteMitDekoration */
    $torteMitDekorationUndKerzen = $this->pimple['torteMitDekorationUndKerzen'];

    // Kerzen aufstecken
    $test = $torteMitDekorationUndKerzen->kerzen();

    // Kerzen anzünden
    $test = $torteMitDekorationUndKerzen->anzuenden();

    // Kerzen auspusten
    $test = $torteMitDekorationUndKerzen->auspusten();

    // Den Frauen klar machen das die Torte zu viele Kalorien hat :-)
    $test = $torteMitDekorationUndKerzen->hasKalorien();

    return;
}

Und letztendlich wird das Templat mit 'templat()' angezeigt.