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

时间:2013-11-09 22:45:18

标签: php mysql oop design-patterns decorator

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

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

好的,但是,我有一些其他实体可以使用相同的装饰器进行修饰,例如,我有另一个名为questions的表,此表有一个指向已提问的客户端的引用,我有另一个名为schedules的表,它有一个客户端的引用。

顺便说一下,我可以用question装饰scheduleClientDecorator

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

我如何做这个抽象,所以我可以随时重用装饰器?

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

2 个答案:

答案 0 :(得分:0)

您总是可以将装饰器类的实例传递给构造函数,该构造函数将告诉它应该如何操作或者它应该模拟的类。您不必将装饰器声明为另一个类的扩展名。

PHP类支持magic methods,可以将调用转发给您的对象正在模拟的类,就像它使用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()可以对属性进行相同的操作。

答案 1 :(得分: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

这只是一个骨架,现实世界的实现一定很棘手。我觉得你最好把你的装饰师分开......