Problem with design pattern decorator

G

Gregory

I recently reviewed the decorator pattern in the GOF book and noticed a
problem.
Let look at the example given in the book. For simplicity I removed
intermediate Decorator class.

// Interface class
class VisualComponent
{
public:
VisualComponent();
virtual void Draw();
virtual void Resize();
// ...
};

// Concrete component
class TextView: public VisualComponent
{
public:
TextView(...);
virtual void Draw();
virtual void Resize();
// ...
private:
// ...
};

// Decorator
class BorderDecorator : public VisualComponent
{
public:
Decorator(VisualComponent*);
virtual void Draw();
virtual void Resize();
// ...
private:
void DrawBorder ( int ) ;
private:
VisualComponent* _component;
int _width;
};

void BorderDecorator :: Draw ()
{
_component->Draw ( ) ;
DrawBorder (_width) ;
}

void BorderDecorator: :Resize ()
{
_component->Resize ( ) ;
}

The BorderDecorator class stores pointer to the VisualComponent object
and delegates
execution of all its methods to the same method of this object. However
for the Draw() method
additional functionality (drawing window border) is added. Good so far.
But what happens if the concrete component TextView implements its
methods using
the decorated Draw() method. Lets say:

void TextView::Resize ()
{
// Do some resing stuff
.....
// Re-draw the window
Draw();
}

Then when invoking BorderDecorator::Resize() method no border will be
drawn for the
TextView window. This breaks our original intention to draw border
every time the TextView is
displayed.
In general it seems that decorator pattern does not behave well when
the decorated
public functionality (method Draw() above) is used in some Concrete
component methods (Resize()).

Does anybody know any solution to the described decorator problem ?

Gregory
 
D

Daniel T.

Gregory said:
I recently reviewed the decorator pattern in the GOF book and
noticed a problem. Let look at the example given in the book. For
simplicity I removed intermediate Decorator class.

// Interface class
class VisualComponent
{
public:
VisualComponent();
virtual void Draw();
virtual void Resize();
// ...
};

// Concrete component
class TextView: public VisualComponent
{
public:
TextView(...);
virtual void Draw();
virtual void Resize();
// ...
private:
// ...
};

// Decorator
class BorderDecorator : public VisualComponent
{
public:
Decorator(VisualComponent*);
virtual void Draw();
virtual void Resize();
// ...
private:
void DrawBorder ( int ) ;
private:
VisualComponent* _component;
int _width;
};

void BorderDecorator :: Draw ()
{
_component->Draw ( ) ;
DrawBorder (_width) ;
}

void BorderDecorator: :Resize ()
{
_component->Resize ( ) ;
}

The BorderDecorator class stores pointer to the VisualComponent
object and delegates execution of all its methods to the same method
of this object. However for the Draw() method additional
functionality (drawing window border) is added. Good so far. But
what happens if the concrete component TextView implements its
methods using the decorated Draw() method. Lets say:

void TextView::Resize ()
{
// Do some resing stuff
.....
// Re-draw the window
Draw();
}

Then when invoking BorderDecorator::Resize() method no border will
be drawn for the TextView window. This breaks our original intention
to draw border every time the TextView is displayed. In general it
seems that decorator pattern does not behave well when the decorated
public functionality (method Draw() above) is used in some Concrete
component methods (Resize()).

Does anybody know any solution to the described decorator problem ?

The obvious solution would be something like this:

void TextView::resize( VisualComponent& parent )
{
// do resizing stuff
parent.Draw();
}

void BorderDecorator::resize()
{
_component->Resize( *this );
}

To implement such a solution, you either need to be able to modify the
classes in question, or the original designer would have to have
thought of the problem and created the appropriate passthrough
functions. A passthrough function would be needed whenever a concrete
class calls a member-function of the ABC.

Of course, the passthrough function will have to be part of the
VisualComponent interface, probably as a protected pure virtual
member-function.
 
D

Daniel T.

Daniel said:
The obvious solution would be something like this:

void TextView::resize( VisualComponent& parent )
{
// do resizing stuff
parent.Draw();
}

void BorderDecorator::resize()
{
_component->Resize( *this );
}

To implement such a solution, you either need to be able to modify the
classes in question, or the original designer would have to have
thought of the problem and created the appropriate passthrough
functions. A passthrough function would be needed whenever a concrete
class calls a member-function of the ABC.

Of course, the passthrough function will have to be part of the
VisualComponent interface, probably as a protected pure virtual
member-function.

I forgot to mention... BorderDecorator will of course also need to
implement resize( VisualComponent&). That should be done like this:

void BorderDecorator::resize( VisualComponent& parent )
{
_component->resize( parent );
}

So the top-level decorator has its 'resize()' function called, the
interum decorators have their 'resize(VisualComponent&)' functions
called, the bottom-level object also has it's
'resize(VisualComponent&)' function called, in which it resizes and
calls parent.draw(). 'parent' will be the top-level object.
 
G

Gregory

"""Daniel T. ÐÉÓÁÌ(Á):
"""
I forgot to mention... BorderDecorator will of course also need to
implement resize( VisualComponent&). That should be done like this:

void BorderDecorator::resize( VisualComponent& parent )
{
_component->resize( parent );
}

So the top-level decorator has its 'resize()' function called, the
interum decorators have their 'resize(VisualComponent&)' functions
called, the bottom-level object also has it's
'resize(VisualComponent&)' function called, in which it resizes and
calls parent.draw(). 'parent' will be the top-level object.

Thanks for your answer. Nice idea ! Let me see if I understand it
correctly. The TextView will finally recieve the top-level decorator
pointer in its Resize(VisualComponent& parent), so the parent is the
top-level decorator and then the Draw() method will be called correctly
whether it was decorated or not. Really elegant solution. Thanks a lot
!

Gregory
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top