1

我可以重复使用装饰器吗?

我有一个ClientDecorator装饰一个具有客户端引用的实体,这个装饰器在调用getClient时获取数据库上的客户端(在它被装饰之前,这个方法返回 clientId,在被装饰之后,它返回一个Client实例)。

好的,但是,我有一些其他实体可以用相同的装饰器进行装饰,例如,我有另一个名为 的表questions,该表有一个指向已提出问题的客户的引用,我还有另一个名为 的表schedules,有一个客户的参考。

顺便说一句,我question可以scheduleClientDecorator.

但是,我也有QuestionDecorator;这家伙装饰了一个Answer,等等。

我如何进行这种抽象,以便我可以随时重用装饰器?

我尝试创建ClientDecorable,QuestionDecorable接口,但没有取得任何进展。

4

2 回答 2

0

您始终可以将装饰器类实例化,将参数传递给构造函数,该构造函数将告诉它应该如何表现或应该模拟什么类。您实际上不必将您的装饰器声明为另一个类的扩展。

PHP 类支持魔术方法,可以将调用转发到您的对象正在模拟的类,就像它使用extends.

例如:

class Client
{
    public function getId() { return 123; }
}

class Decorator
{
    private $instance = null;

    public function __construct($class)
    {
        $this->instance = new $class();
    }

    public function __call($method, $params) // magic method
    {
        return call_user_func_array(array($this->instance, $method), $params);
    }
}

$object = Decorator('Client');
echo $object->getId(); // 123

__call()当您尝试访问不属于该类的方法时,将调用魔术方法Decorator。通过使用魔术方法__get()__set().

于 2013-11-09T22:55:09.377 回答
0

这真是一个棘手的问题。我可以找到一个解决方案,但它是一种 McGiver 风格......适用于 PHP 5.4+(是的,特征)。

<?php
interface Decorable
{

    public function getTarget();

}


interface ClientDecorable extends Decorable
{

    public function getClient();

}


interface LogDecorable extends Decorable
{

    public function getLog();

}

abstract class AbstractDecorator implements Decorable 
{

    private $target;

    public function __construct(ClientDecorable $target)
    {
        $this->target = $target;
    }

    public function getTarget()
    {
        // I'll be able to access the leaf node of my decorator single way 'tree'
        return $this->target->getTarget();
    }

    public function __call($method, $args) {

        $reflected = new ReflectionClass($this->target);
        if ($reflected->hasMethod($method)) {
            return call_user_func_array([$this->target, $method], $args);
        }
    }

}

class ClientDecorator extends AbstractDecorator implements ClientDecorable
{
    public function __construct(Decorable $target) {
        if (! $target->getTarget() instanceof ClientDecorable) {
            throw new Exception('Must be an instance de ClientDecorable');
        }
        parent::__construct($target);
    }
    public function getClient()
    {
        return new Client($this->getTarget()->getClient());
    }

}

class LogDecorator extends AbstractDecorator implements LogDecorable
{
    public function __construct(Decorable $target) {
        if (! $target->getTarget() instanceof LogDecorable) {
            throw new Exception('Must be an instance de LogDecorable');
        }
        parent::__construct($target);
    }

    public function getLog()
    {
        return new Log($this->getTarget()->getLog());
    }

}

abstract class AbstractTarget implements Decorable
{
    // this does the trick
    public function getTarget() { return $this; }
}

trait ClientDecorableTrait {
    public function getClient()
    {
        return $this->client;
    }
}

trait LogDecorableTrait {
    public function getLog()
    {
        return $this->log;
    }
}



class Payment extends AbstractTarget implements ClientDecorable, LogDecorable
{
    use ClientDecorableTrait;
    use LogDecorableTrait;

    private $client = 1;
    private $log = 101;
}

class Sale extends AbstractTarget implements ClientDecorable
{
    use ClientDecorableTrait;

    private $client = 2;

}

class Client
{

    // ...

}


class Log
{

    // ...

}

$sale = new Sale();
var_dump($sale->getClient());
$saleDec = new ClientDecorator($sale);
var_dump($saleDec->getClient());

$payment = new Payment();
var_dump($payment->getClient());
$paymentDec = new ClientDecorator($payment);
var_dump($paymentDec->getClient());

var_dump($paymentDec->getLog());
$paymentDecTwice = new LogDecorator($paymentDec);
var_dump($paymentDecTwice->getLog());

$saleDecTwice = new LogDecorator($saleDec); // will throw an exception

这只是一个骨架,现实世界的实现一定很棘手。我认为你最好让你的装饰器分开......

于 2013-11-12T23:52:37.677 回答