Как реализовать декоратор в 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() {
// ...
}
}
В приведенном выше коде предполагается добавить некоторую дополнительную работу в массив.
Ответы
Ответ 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