Как я могу реализовать шаблон декоратора, не заставляя кого-то переопределять все? - PullRequest
1 голос
/ 25 октября 2010

Отмена этого примера из Википедии :

// the Window interface
interface Window {
    public void draw(); // draws the Window
    public String getDescription(); // returns a description of the Window
}

// implementation of a simple Window without any scrollbars
class SimpleWindow implements Window {
    public void draw() {
        // draw window
    }

    public String getDescription() {
        return "simple window";
    }
}

// abstract decorator class - note that it implements Window
abstract class WindowDecorator implements Window {
    protected Window decoratedWindow; // the Window being decorated

    public WindowDecorator (Window decoratedWindow) {
        this.decoratedWindow = decoratedWindow;
    }
   public void draw() {
        decoratedWindow.draw();
    }
}

Как бы я, например, разрешил пользователю реализовать декоратор, который украшает draw, но не getDescription? В моем реальном коде есть 5 возможных методов, которыми они могли бы украсить.

Как я вижу, у меня есть 3 варианта:

  • Поместите все 5 методов в интерфейс. Недостатком является то, что это заставит их реализовать 4 метода, которые будут просто вызывать родителя.
  • Создайте 5 интерфейсов, по одному для каждого метода. Немного неуклюжий.
  • Не используйте интерфейс вообще. Потерять проектный контракт.

Есть ли другие варианты? Если нет, то что из вышеперечисленного будет лучшим выбором?

Я использую PHP.

Ответы [ 3 ]

1 голос
/ 26 октября 2010

Вы можете использовать наследование конструктора PHP и создать абстрактную реализацию декоратора по умолчанию AbstractWindowDecorator, которая просто получает оформленное окно и перенаправляет все вызовы к нему. И теперь, при реализации новых декораторов, просто наследуйте от AbstractWindowDecorator и переопределяйте только те методы, которые вам нужны.

// the Window interface
interface Window
{
    public function draw(); // draws the Window
    public function getDescription(); // returns a description of the Window
}

// implementation of a simple Window without any scrollbars
class SimpleWindow implements Window
{
    public function draw()
    {
        // draw window
    }

    public function getDescription()
    {
        return "simple window";
    }
}

// abstract decorator class - note that it implements Window
abstract class AbstractWindowDecorator implements Window
{
    protected $decoratedWindow; // the Window being decorated

    public final function __construct ($decoratedWindow)
    {
        $this->decoratedWindow = $decoratedWindow;
    }

    public function draw()
    {
        return $this->decoratedWindow->draw();
    }

    public function getDescription()
    {
        return $this->decoratedWindow->getDescription();
    }
}

// all decorators can now
class SampleWindowDecorator extends AbstractWindowDecorator
{
   public function draw()
   {
       // do something other;
   }
}

AbstractWindowDecorator все еще реализует Window, поэтому у вас есть право на контракт, и вам не нужно реализовывать все методы. И более того, все декораторы, созданные «по-старому», без абстрактного класса, тоже будут вписываться в этот шаблон.

Инстанцирование декоратора такое же, как и в вашем примере:

$w = new SimpleWindow();
$d = new SampleWindowDecorator($w);
echo $d->getDescription(); // "simple window"

(На самом деле вы можете сделать это даже без наследования конструктора, но вам нужно явно создать конструктор для каждого декоратора.)

1 голос
/ 25 октября 2010

Если вы разрабатываете на C ++, вы можете использовать шаблоны.

Вместо того, чтобы извлекать WindowDecorator из Window, вы можете извлечь его из класса, заданного в качестве параметра шаблона, например:

template <typename T>
class WindowDecorator : public T
   {
   public:
      // Put here what you want to decorate
      // Every method not implemented here is simply forwarded to T
   };

WindowDecorator<MyWindow> mywindow;

Конечно, это делает некоторые предположения:

  • Экземпляр, который вы хотите украсить, должен быть «создан» вместе с декоратором
  • Все классы, которые вы хотите украсить, имеют конструктор с одинаковыми аргументами

Альтернативой может быть простой класс пересылки, который перенаправляет все вызовы в декорированный класс, например:

class WindowForwarder
   {
   public:
      WindowForwarder (Window &decoratedWindow) : m_decoratedWindow(decoratedWindow) {}
      virtual void draw() {m_decoratedWindow.draw();}
      // add all forwarding methods here
   private:
      Window &m_decoratedWindow;
   };

Тогда вы можете иметь определенные декораторы в качестве подклассов. Просто наследуйте от WindowForwarder и реализуйте то, что вы хотите отменить. Все, что вы не отменили, будет перенаправлено в оформленное окно, пока WindowForwarder перенаправляет вызовы.

0 голосов
/ 29 октября 2010

Я не вижу большой картины декоратора внутри вашей проблемы. Я хотел бы увидеть больше Шаблон метода Pattern

Если единственный метод, который должен быть украшен, это рисование, тогда вам нужен объект Painter, а затем вы указываете разную логику на разных художников.

...