如何在PHP中实现装饰器?

时间:2009-06-04 03:24:50

标签: php oop decorator

假设有一个名为“Class_A”的类,它有一个名为“func”的成员函数。

我希望“func”通过将Class_A包装在装饰器类中来完成一些额外的工作。

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

有人能举个例子吗?我从未在PHP上使用过OO。

以下版本是否正确?

class Decorator
{
    protected $jobs2do;

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

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

上面的代码打算为数组添加一些额外的工作。

5 个答案:

答案 0 :(得分:43)

我建议您还为装饰器和要装饰的对象创建统一的接口(甚至是抽象基类)。

要继续上面的示例,前提是您可以使用以下内容:

interface IDecoratedText
{
    public function __toString();
}

然后当然修改 {/ strong> TextLeetText来实现界面。

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

}

为什么要使用界面?

因为那时你可以添加任意数量的装饰器,并确保每个装饰器(或要装饰的对象)都具有所有必需的功能。

答案 1 :(得分:35)

这非常简单,特别是在像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

您也可以查看wikipedia article

答案 2 :(得分:23)

这些答案都没有正确而优雅地实现Decorator。 mrmonkington的答案很接近,但您不需要使用反射来在PHP中实现Decorator模式。在另一个帖子中,@Gordon shows how to use a decorator for logging SOAP activity。他是这样做的:

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

答案 3 :(得分:6)

我想使用装饰来鼓励同事更多地使用缓存,并且受到使用PHP反射实验的漂亮Python语法的启发,以伪造这种语言功能(我必须强调'假')。这是一种有用的方法。这是一个例子:

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/

答案 4 :(得分:-2)

Decorator的一个关键和独特功能是一个抽象类扩展另一个。 Decorator参与者既是组件参与者的包装器又是扩展组件参与者。

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

我相信这是四人帮目录中发生这种情况的唯一模式。 Decorator可以轻松地在不更改对象的情况下向对象添加属性。有关简单,准确和清晰的示例,请参阅:

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