Problem with design pattern decorator

Discussion in 'C++' started by Gregory, Dec 14, 2006.

  1. Gregory

    Gregory Guest

    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
     
    Gregory, Dec 14, 2006
    #1
    1. Advertising

  2. Gregory

    Daniel T. Guest

    Gregory wrote:
    >
    > 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.
     
    Daniel T., Dec 15, 2006
    #2
    1. Advertising

  3. Gregory

    Daniel T. Guest

    Daniel T. wrote:
    >
    > 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.
     
    Daniel T., Dec 15, 2006
    #3
  4. Gregory

    Gregory Guest

    """Daniel T. ÐÉÓÁÌ(Á):
    """
    > Daniel T. wrote:
    > >
    > > 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.


    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
     
    Gregory, Dec 15, 2006
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jean Lutrin
    Replies:
    8
    Views:
    662
    Chris Uppal
    Nov 18, 2004
  2. Chris Forone

    design q: decorator pattern

    Chris Forone, Jul 10, 2008, in forum: C++
    Replies:
    10
    Views:
    690
    James Kanze
    Jul 11, 2008
  3. Pallav singh
    Replies:
    0
    Views:
    364
    Pallav singh
    Jan 22, 2012
  4. Pallav singh
    Replies:
    0
    Views:
    404
    Pallav singh
    Jan 22, 2012
  5. Pallav singh
    Replies:
    1
    Views:
    452
    Peter Remmers
    Jan 22, 2012
Loading...

Share This Page