Подтвердить что ты не робот

Как реализовать декоратор в PHP?

Предположим, что существует класс под названием "Class_A", он имеет функцию-член, называемую "func".

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

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

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

Правильно ли следующая версия?

class Decorator
{
    protected $jobs2do;

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

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

В приведенном выше коде предполагается добавить некоторую дополнительную работу в массив.

4b9b3361

Ответ 1

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

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

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());
    }

}

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

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

Ответ 2

Это довольно легко, особенно на динамически типизированном языке, таком как 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

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

Ответ 3

Ни один из этих ответов не реализует 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);
    }   
}

Ответ 4

Я хотел использовать украшение, чтобы побудить коллег использовать кеширование больше и вдохновлялось хорошим синтаксисом 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 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/

Ответ 5

Ключевой и уникальной особенностью 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