如何在 PHP 中实现装饰器?
Posted
技术标签:
【中文标题】如何在 PHP 中实现装饰器?【英文标题】:How to implement a decorator in PHP? 【发布时间】:2010-10-31 05:04:27 【问题描述】:假设有一个名为“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()
// ...
上面的代码打算给数组做一些额外的工作。
【问题讨论】:
你应该初始化你的变量:protected $jobs2do = array(); 但这仍然不是标准版本,也许应该使用'call_user_func'之类的东西 哦,没看到!是的,当然,在 PHP 5.3 之前没有闭包。你绝对应该看看 PHP 中的回调:us.php.net/call-user-func 我写了这个装饰器摘要来支持灵活的装饰器:gist.github.com/CMCDragonkai/6896642 派生自jrgns.net/decorator-pattern-implemented-properly-in-php/… sourcemaking.com/design_patterns/decorator/php 符合要求。会回答,但我不想只是复制和粘贴他的作品。 【参考方案1】:我建议你也为装饰器和你想要装饰的对象创建一个统一的接口(甚至是抽象基类)。
如果你可以有类似的东西,继续上面的例子:
interface IDecoratedText
public function __toString();
那么当然修改bothText
和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 中特别容易:你不需要这些东西 :) 如果你真的想要类型安全,你应该使用另一种语言。 我没有看到在这里使用界面的好处 谢谢,但是如何将新成员函数堆积到数组中?似乎我当前的版本会引起警告。 接口本身与类型安全无关。它有助于调试并提供清晰度。调试示例:您将收到“对象未实现 IDecoratedText 接口”,而不是更通用的“方法不存在”错误。更好的设计: 1. 这种设计还清楚地向未来的开发人员(没有无关的 cmets)传达了这个装饰器在什么类型的对象上工作(而不是通用的 $text 属性)。 2. 接口明确了未来对象(装饰器或“装饰者”)需要实现哪些方法。 还有提供语义检查的 IDE(例如 PhpStorm)。通过将类型信息(例如 IDecoratedText)添加到变量中,PhpStorm 将指出您是否正在调用该类型提供的方法。【参考方案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
您可能也想看看wikipedia article。
【讨论】:
谢谢,但是如何将新成员函数堆积到数组中?似乎我当前的版本会引起警告。 为什么 $string 和 $text 不是私有的? 公共函数 __construct($text) 不清楚。 $text 是什么意思?怎么看一眼就知道里面应该放什么?犯错太容易了。 抱歉,我好像忘记记录我的代码 sn-p。无论如何,这里的标准是什么?我在哪里可以找到这个论坛的编码指南?我们使用 doxygen 风格的 doc cmets 还是 javadoc?还是针对不同的语言有不同的指导方针? 对于Decorator
,难道你不能直接从LeetText
调用任何Text
方法吗?它应该添加您的功能,但您的示例也带走了功能。例如。如果你在Text
上有一个reverse()
方法,你会如何从$text
调用它?我认为您需要使用__call()
并将方法调用传回protected $text
才能成为Decorator
。【参考方案3】:
这些答案都没有正确而优雅地实现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);
【讨论】:
您应该改进 __call() 方法以支持实现 fluent-api 模式的对象。类似$result = call_user_func_array(array($this->o, $method), $args); return $result === $this->o ? $this : $result;
请解释一下$function_name的用法和工作流程【参考方案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'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/gamernetwork/EggCup
【讨论】:
这更像是一种代理模式!!【参考方案5】:装饰器的一个关键和独特的特性是一个抽象类扩展另一个抽象类。装饰器参与者既是组件参与者的包装器,又是组件参与者的扩展。
<?php
abstract class Decorator extends IComponent
//public function getDescription()
?>
我相信这是四人帮目录中唯一出现这种情况的模式。装饰器可以很容易地在不更改对象的情况下向对象添加属性。有关简单、准确和清晰的示例,请参阅:
http://www.php5dp.com/php-decorator-design-pattern-accessorizing-your-classes/#more-32
【讨论】:
以上是关于如何在 PHP 中实现装饰器?的主要内容,如果未能解决你的问题,请参考以下文章