我可以重复使用装饰器吗?
我有ClientDecorator
来装饰一个有客户端引用的实体,这个装饰器在调用 getClient 的数据库上获取客户端(在它被装饰之前,此方法返回clientId,在装饰后,返回客户端)的实例。
好的,但是,我有一些其他实体可以使用相同的装饰器进行修饰,例如,我有另一个名为questions
的表,此表有一个指向已提问的客户端的引用,我有另一个名为schedules
的表,它有一个客户端的引用。
顺便说一下,我可以用question
装饰schedule
和ClientDecorator
。
但是,我也有QuestionDecorator
;这家伙装饰Answer
等等。
我如何做这个抽象,所以我可以随时重用装饰器?
我尝试创建ClientDecorable
,QuestionDecorable
接口,但没有取得任何进展。
答案 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
这只是一个骨架,现实世界的实现一定很棘手。我觉得你最好把你的装饰师分开......