2009-06-04 62 views
28

假设有一个名为“Class_A”的类,它具有一个名为“func”的成员函数。如何在PHP中实现装饰器?

我希望“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() { 
     // ... 
    } 
} 

上面的代码打算把一些额外的工作放到数组中。

+1

你应该初始化您的变量:保护$ jobs2do =阵列(); – soulmerge 2009-06-04 10:18:41

+0

但这仍然不是标准版本,也许应该使用像'call_user_func' – omg 2009-06-04 10:33:36

+0

哦,没有看到!当然,PHP 5.3之前没有关闭。你一定要看看PHP中的回调函数: http://us.php.net/call-user-func – soulmerge 2009-06-04 10:38:56

回答

34

这是很容易的,特别是在像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了。

+0

谢谢,但是如何将一个新的成员函数堆到一个数组?看起来我的当前版本会引起警告。 – omg 2009-06-04 09:57:20

+0

为什么$ string和$ text不是私有的? – 2009-06-04 10:20:44

+0

你当然可以把它变成私人的:) – omg 2009-06-04 10:32:59

38

我建议你也为装饰者和你想装饰的物体创建一个统一的界面(或者甚至抽象基类)。

要继续提供,你可以有像上面的例子:

interface IDecoratedText 
{ 
    public function __toString(); 
} 

那当然修改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

这就是为什么我说这在PHP中特别容易:你不需要这样的东西:) 如果你真的想要类型安全,你应该使用另一种语言。 – soulmerge 2009-06-04 05:01:33

+0

我没有看到在这里使用接口的好处 – 2009-06-04 08:45:07

+0

谢谢,但如何将一个新的成员函数堆栈到一个数组?似乎我的当前版本将导致警告。 – omg 2009-06-04 09:57:05

5

我想用装饰来鼓励同事使用缓存更多,并且灵感来自用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/

+0

这是更多类型的代理模式! – 2014-02-05 17:46:04

20

以上答案都不正确,优雅实现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

您应该改进__call()方法来支持实现fluent-api模式的对象。类似于 '$ result = call_user_func_array(array($ this-> o,$ method),$ args); return $ result === $ this-> o? $ this:$ result;' – Nat 2017-03-02 08:43:50

+0

请解释$ function_name的用法和工作流程 – Massimo 2017-04-23 00:52:25

-2

的装饰的一个重要和独特的特点是一个抽象类扩展另一个。装饰参与者既是组件参与者的包装也是扩展组件。

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

我相信这是在四人帮的目录中发生这种情况的唯一模式。装饰器可以很容易地向对象添加属性而不用改变对象。对于简单,准确和清晰的例子中看到:

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

相关问题