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

Предположим, есть класс под названиемClass_Amsgstr "есть функция-член"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 ответов

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

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

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

}

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам