Как реализовать декоратор в PHP? - PullRequest
31 голосов
/ 04 июня 2009

Предположим, есть класс с именем "Class_A", у него есть функция-член с именем "func".

Я хочу, чтобы "func" проделал дополнительную работу, поместив Class_A в класс декоратора.

$worker = new Decorator(new Original());

Может кто-нибудь привести пример? Я никогда не использовал ОО с PHP.

Верна ли следующая версия?

class Decorator
{
    protected $jobs2do;

    public function __construct($string) {
        $this->jobs2do[] = $this->do;
    }

    public function do() {
        // ...
    }
}

Приведенный выше код предназначен для дополнительной работы с массивом.

Ответы [ 5 ]

43 голосов
/ 04 июня 2009

Я бы посоветовал вам также создать унифицированный интерфейс (или даже абстрактный базовый класс) для декораторов и объектов, которые вы хотите оформить.

Чтобы продолжить приведенный выше пример, у вас может быть что-то вроде:

interface IDecoratedText
{
    public function __toString();
}

Затем, конечно, измените оба Text и LeetText для реализации интерфейса.

class Text implements IDecoratedText
{
...//same implementation as above
}

class LeetText implements IDecoratedText
{    
    protected $text;

    public function __construct(IDecoratedText $text) {
        $this->text = $text;
    }

    public function __toString() {
        return str_replace(array('e', 'i', 'l', 't', 'o'), array(3, 1, 1, 7, 0), $this->text->toString());
    }

}

Зачем использовать интерфейс?

Потому что тогда вы можете добавить столько декораторов, сколько захотите, и быть уверенными, что каждый декоратор (или объект, который нужно декорировать) будет иметь все необходимые функции.

35 голосов
/ 04 июня 2009

Это довольно легко, особенно в динамически типизированном языке, таком как PHP:

class Text {

    protected $string;

    /**
     * @param string $string
     */
    public function __construct($string) {
        $this->string = $string;
    }

    public function __toString() {
        return $this->string;
    }
}

class LeetText {

    protected $text;

    /**
     * @param Text $text A Text object.
     */
    public function __construct($text) {
        $this->text = $text;
    }

    public function __toString() {
        return strtr($this->text->__toString(), 'eilto', '31170');
    }
}

$text = new LeetText(new Text('Hello world'));
echo $text; // H3110 w0r1d

Возможно, вы захотите взглянуть и на статью википедии .

23 голосов
/ 11 марта 2013

Ни один из этих ответов не реализует Decorator правильно и элегантно. Ответ mrmonkington близок, но вам не нужно использовать рефлексию, чтобы ввести шаблон Decorator в PHP. В другом потоке @ Gordon показывает, как использовать декоратор для регистрации активности SOAP . Вот как он это делает:

class SoapClientLogger
{
    protected $soapClient;

    // this is standard. Use your constuctor to set up a reference to the decorated object.
    public function __construct(SoapClient $client)
    {
        $this->soapClient = $client;
    }

    ... overridden and / or new methods here ...

    // route all other method calls directly to soapClient
    public function __call($method, $args)
    {
        // you could also add method_exists check here
        return call_user_func_array(array($this->soapClient, $method), $args);
    }
}

И это небольшая модификация, где вы можете передать желаемую функциональность конструктору:

class Decorator {

    private $o;

    public function __construct($object, $function_name, $function) {
        $this->o = $object;
        $this->$function_name = $function;
    }
    public function __call($method, $args)
    {
        if (!method_exists($this->o, $method)) {
            throw new Exception("Undefined method $method attempt in the Url class here.");
        }
        return call_user_func_array(array($this->o, $method), $args);
    }   
}
6 голосов
/ 17 августа 2011

Я хотел использовать художественное оформление, чтобы побудить коллег больше использовать кеширование, и вдохновленный красивым синтаксисом Python, экспериментировавшим с PHP-отражением для фальсификации этой языковой функции (и я должен подчеркнуть «фальшивку»). Это был полезный подход. Вот пример:

class MrClass { 
  /**
   * decoratorname-paramname: 50
   * decoratorname-paramname2: 30
   */
   public function a_method( $args ) {
     // do some stuff
   }
}


class DecoratorClass {
  public function __construct( $obj ) {
     $this->obj = $obj;
     $this->refl = new ReflectionClass( $obj );
  }
  public function __call( $name, $args ) {
    $method = $this->refl->getMethod( $name );
    // get method's doccomment
    $com = trim( $method->getDocComment() );
    // extract decorator params from $com
    $ret = call_user_func_array( array( $this->obj, $name), $args );
    // perhaps modify $ret based on things found in $com
    return $ret;
}

Лучшие примеры с примерами кэширования здесь: https://github.com/mrmonkington/EggCup/

0 голосов
/ 31 июля 2014

Ключевой и уникальной особенностью Decorator является то, что один абстрактный класс расширяет другой. Участник Decorator является и оболочкой, и расширяет участника Component.

<?php
abstract class Decorator extends IComponent
{
    //public function getDescription() { }
}
?>

Я полагаю, что это единственный шаблон, где это происходит в каталоге «Банды четырех». Декоратор позволяет легко добавлять свойства к объекту без изменения объекта. Простой, точный и понятный пример:

http://www.php5dp.com/php-decorator-design-pattern-accessorizing-your-classes/#more-32

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...