views:

52

answers:

3

Lifting this example from Wikipedia:

// 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();
    }
}

How would I, for example, allow a user to implement a decorator that decorates draw but not getDescription? In my actual code, there are 5 possible methods they could decorate.

The way I see it, I have 3 options:

  • Put all 5 methods on the interface. The downside is this would force them to implement to 4 methods that would simply be calling the parent.
  • Create 5 interfaces, one for each method. A bit clumsy.
  • Don't use an interface at all. Lose the design contract.

Are there any other options? If not, which of the above would be the best choice?

I'm using PHP.

A: 

If you're developing in C++ you could probably use templates.

Instead of deriving WindowDecorator from Window, you could derive it from the class given as template parameter, like this:

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;

Of course, this makes some assumptions:

  • The instance that you want to decorate should be 'created' together with the decorator
  • All classes you want to decorate have a constructor with the same arguments

An alternative could be to have a simple 'forwarding class', that forwards all calls to the decorated class, like this:

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

Then you can have specific decorators as subclasses. Just inherit from the WindowForwarder and implement what you want to overrule. Everything that you have not overruled will be forwarded to the decorated window, as long as the WindowForwarder forwards the calls.

Patrick
Sorry, I should have specified, I'm using PHP.
ryeguy
+1  A: 

You can make use of PHP's constructor inheritance and create an abstract, default decorator implementation AbstractWindowDecorator, which just gets the decorated Window and forward all calls to it. And now, when implementing new decorators, just inherit from AbstractWindowDecorator and override only these methods you need.

// 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 still implements Window, so you have your contract right and you don't need to implement all methods. And moreover, all decorators created "old way", without abstract class, will fit into this pattern, too.

Decorator's instantiation is just like it was in your example:

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

(In fact, you can do it even without constructor inheritance, but you'll need to explicitly create an constructor for each decorator.)

A.
A: 

I don't see much the decorator pattern inside your problem. I would see more a Template Method Pattern

If the only method that needs to be decorated is the draw, then you need a Painter Object, and then you specify the different logic into different Painters.

fabrizioM