【问题标题】:Can I reuse decorators?我可以重复使用装饰器吗?
【发布时间】:2023-03-16 11:09:01
【问题描述】:

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

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

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

顺便说一句,我可以用ClientDecorator装饰questionschedule

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

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

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

【问题讨论】:

  • 你需要定义接口。就像你写的一样。您的装饰器将只接受实现已定义接口的组件。这是要走的路。发布您拥有的代码。
  • 给我一个例子@busypeoples
  • 你有没有注意到 *Decorator 对于装饰者来说是个坏名字?你应该给事物起描述性的名字......如果我们不知道你的装饰器会改变哪些功能,很难说你能做什么或不能做什么......
  • @HenriqueBarcelos,在这种特定情况下,它正在装饰一个实体,例如:问题,->getClient ...它返回客户端ID,真的吗?装饰后,返回对应客户端的实例。
  • 但是你的装饰器改变了什么?

标签: php mysql oop design-patterns decorator


【解决方案1】:

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

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

当您尝试访问不属于类Decorator 的方法时,将调用魔术方法__call()。使用魔术方法__get()__set() 可以对属性进行同样的操作。

【讨论】:

  • 这不是丑陋的方式吗?理论上它很简单,例如:Client、Payment、Log ... 所以:Log 可以用 Client 装饰,Payment 可以用 Client 和 Log 装饰...明白了吗?我在代码中看不到这个:(
  • 一点也不。我只是写了一个例子给你一个想法。
  • 当然,你愿意扩展的类必须有共同的方法。如果它们完全不同,那么装饰器的代码甚至对它都没有意义。
  • 是的,我认为你是对的......即使装饰器模式也必须是特定的,需要装饰的东西,而且应该是。如果我尝试在其他装饰器中重用这些装饰器,这真的没有意义,例如:$x = new ClientDecorator(new LogDecorator(new ConcretePayment())); 如果你看到,所有涉及的对象都有一种关联类型,但是:客户端装饰日志装饰支付,但是当支付被装饰时由客户?永远,永远不会以可重复使用的方式,我想。
  • 看起来你过度使用装饰器,也许你应该尝试不同的方法。
【解决方案2】:

这是一个非常棘手的问题。我可以找到一个解决方案,但它有点像 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

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

【讨论】:

    猜你喜欢
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2011-02-27
    • 2018-05-31
    • 1970-01-01
    • 2016-01-13
    相关资源
    最近更新 更多