2013-11-09 4 views
1

Можно ли повторно использовать декораторы?Могу ли я использовать декораторы?

У меня есть ClientDecorator украсить объект, который имеет ссылку на клиент, это декоратор получает клиент на базе по вызову getClient (прежде чем он будет оформлен, этот метод возвращает ClientId, после того, как оформлен, он возвращается экземпляр Клиент).

Хорошо, но у меня есть другие объекты, которые могут быть украшены одним и тем же декоратором, например, у меня есть другая таблица с именем questions, эта таблица имеет ссылку, указывающую на клиента, задавшего вопрос, и я есть другая таблица с именем schedules, которая имеет ссылку клиента.

Кстати, я могу украсить question и schedule с ClientDecorator.

Но у меня есть QuestionDecorator; этот парень украшает Answer и т. д.

Как я могу сделать эту абстракцию, чтобы я мог повторно использовать декораторов всякий раз, когда захочу?

Я пытался создать ClientDecorable, QuestionDecorable интерфейсы, но они не достигли прогресса.

+0

Вам нужно определить интерфейсы. Так же, как вы писали. Ваш декоратор будет принимать только компоненты, реализующие определенные интерфейсы. Это путь. Отправьте код, который у вас есть. – busypeoples

+0

Gimme example @busypeoples –

+0

Вы заметили, что '* Decorator' является плохим именем для декоратора? Вы должны дать описание описательных имен ... Трудно сказать, что вы можете или не можете сделать, если не знаете, какие функции изменит ваш декоратор ... –

ответ

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().

+0

Это не уродливый способ? теоретически это просто, например: клиент, оплата, журнал ... так: журнал может быть украшен клиентом, оплата может быть оформлена клиентом и журналом ... получить представление? Я не вижу этого в коде :( –

+0

Нет. Я просто написал пример, чтобы дать вам идею. – Havenard

+0

Но, конечно, классы, которые вы готовы продлить, должны иметь общие методы. Если они совершенно разные, код вашего декоратора даже не имеет смысла. – Havenard

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 

Это всего лишь скелет, реализация реального мира должна быть сложной задачей. Я думаю, вам лучше оставить ваши декораторы отделенными ...

Смежные вопросы