Click here to Skip to main content
Click here to Skip to main content

Tagged as

Building Decorator Chains

, 22 Oct 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
The article explains a method, how flexible and extendible decorator chains can be built in a generic way. Its power is best seen if it is used with boost::factory and boost::bind.

Introduction

The decorator pattern is a classical design pattern in modern object-oriented development. Usually, it is used to extend the functionality of existing objects. In this article, I will introduce a simple method to build flexible and extendable chains of decorators using modern C++.

The method makes strong use of the boost::factory template. In combination with boost::bind and boost::ref, it is a powerful and easy to use tool for the creation of decorator chains.

The code I will show here is a basic idea of how to manage and create such chains. I have developed it for a specific application and I started to I love it. Nevertheless, it is just one possible way. If you have different approaches, please let me know. Of course, discussions are highly welcome.

Decorators

Decorator chains are usually built with constructor arguments of the decorator:

#include <iostream>

using namespace std;

class abstract_class
{
public:
    virtual ~abstract_class( void ) { }
    virtual void do_something( void ) = 0;
};

class concrete_class : public abstract_class
{
public:
    void do_something( void ) { cout << "concrete_class_a\n"; }
};

class decorator : public abstract_class
{
    abstract_class *m_decorated_class;
public:
    decorator( abstract_class *decorated_class ) : 
		m_decorated_class( decorated_class ) { }
    void do_something( void )
    {
        m_decorated_class->do_something();
        cout << "decorator_a\n";
    }
};

int main( int argc , char **argv )
{
    concrete_class c;
    decorator d( &c );
    d.do_something();

    return 0;
}

Note that the component which should be decorated is passed as a pointer and that the destruction of this object is not handled by the decorator. Of course, it might be possible to pass the component as reference or to give the ownership of the component to the decorator. Nevertheless, you have to remember the head element, that is the decorator which is the last in the chain, and which is used to called do_something:

int main( int argc , char **argv )
{
    concrete_class c;     // c is the head element
    decorator d1( &c );   // d1 is now the head element
    decorator d2( &d1 );  // d2 is now the head element
    decorator d3( &d2 );  // d3 is now the head element
    d3.do_something();    // use the head element to do_something

    return 0;
}

Remember the head element is simple in the above example. But it might become cumberstone in other applications. For example, if the decorator chain is hidden deep in some other classes, or if you have to construct the chain dynamically from some configuration file, etc. This is where decorator_chain comes into play.

Decorator Chain

The main idea is to encapsulate the creation and instantiation of the decorator objects as well as the component which is decorated into a single class:

template< class Class , class DecoratorCategory = pointer_decorator_category >
class decorator_chain
{
public:

    typedef Class class_type;
    typedef DecoratorCategory category_type;

    decorator_chain( bool owner_of_class = false , bool owner_of_decorators = false )
    {
        // ...
    }

    ~decorator_chain( void )
    {
        // ...
    }

    template< class Factory >
    void create_class( Factory make_class = Factory() )
    {
        // ...
    }

    template< class Factory >
    void decorate( Factory make_class = Factory() )
    {
        // ...
    }

    class_type& get_class_reference( void ) { return *m_current; }
    const class_type& get_class_reference( void ) const { return *m_current; }

    class_type* get_class_pointer( void ) { return m_current; }
    const class_type* get_class_pointer( void ) const { return m_current; }
};

The class takes two templates arguments. One is the type of the component which should be decorated. This type can be abstract. The second argument indicates whether the objects which should be decorated are passed as pointers or as references to the decorator. The constructor has two arguments, which specify if the tail element is owned by the decorator_chain and if the decorators are owned by decorator_chain. If they are owned, decorator_chain will destroy these objects if the destructor of decorator_chain is called, otherwise not. The head (current) element can be accessed via get_class_reference or get_class_pointer.

The main work is done by create_class and decorate. The first method creates the tail element of the chain. It assumes that Factory is a function object, returning a new instance of class_type. This function object does not take any arguments. A simple example is:

abstract_class* make_concrete_class( void )
{
    return new concrete_class;
}

The second method creates a decorator object. Here, Factory is a function object taking one argument - the component which should be decorated. A simple example for such a function is:

abstract_class* make_decorator( abstract_class *ac )
{
    return new decorator( ac );
}

You might wonder about this strange way of calling new, but here the boost::factory template comes into play. It encapsulates a call of new, such that the above example can be written as:

int main( int argc , char **argv )
{
    decorator_chain< abstract_class > db;
    db.create_class( boost::factory< concrete_class* >() );
    db.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    db.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    db.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    db.get_class_reference().do_something();

    return 0;
}

Note, how easily you can build a function (taking the decorating state as argument) with the help of bind and the placeholder _1.

You can also use boost::bind for argument normalization, hence to pass values to the constructor. boost::ref can be used to pass references:

class int_decorator : public abstract_class
{
    abstract_class *m_decorated_class;
    int m_val;
public:
    int_decorator( abstract_class *decorated_class , int val ) : 
	m_decorated_class( decorated_class ) , m_val( val ) { }
    void do_something( void )
    {
        m_decorated_class->do_something();
        cout << "int_decorator : " << m_val << "\n";
    }
};

class string_decorator : public abstract_class
{
    abstract_class *m_decorated_class;
    string &m_val;
public:
    string_decorator( abstract_class *decorated_class , string &val ) : 
		m_decorated_class( decorated_class ) , m_val( val ) { }
    void do_something( void )
    {
        m_decorated_class->do_something();
        cout << "string_decorator " << m_val << "\n";
    }
};

int main( int argc , char **argv )
{
    string str = "Hello world!";
    decorator_chain< abstract_class > dc;
    dc.create_class( boost::factory< concrete_class* >() );
    dc.decorate( boost::bind( boost::factory< decorator* >() , _1 ) );
    dc.decorate( boost::bind( boost::factory< int_decorator* >() , _1 , 10 ) );
    dc.decorate( boost::bind( boost::factory< string_decorator* >() , 
		_1 , boost::ref( str ) ) );
    dc.get_class_reference().do_something();

    return 0;
}

If you have to call the constructor of the decorator with the reference to the decorating object, you can set the second template argument of decorator_chain to reference_decorator_category.

Conclusions

In this article, I have shown a simple method to construct complex chains of decorators. It heavily uses boost::factory and boost::bind. Possible extensions could include:

  • Dynamic decorator chains. This requires that the decorators could be set separately, similar to decorator.set_decorated_state(decorated_state).
  • The chain could implement the abstract type, such that it behaves like the component.

If you have some interesting points, criticisms, or suggestions, please let me know.

How to Compile?

Compiling the examples is simple under Linux:

g++ -I/path/to/boost example1.cpp -o example1
# or
g++ -I/path/to/boost example2.cpp -o example2

where /path/to/boost points to directory of the boost libraries. You need at least version 1.43.

References

History

  • 20.10.2010 Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

headmyshoulder

Germany Germany
No Biography provided

Comments and Discussions

 
GeneralMemoryLeaks, imperfect container usage PinmemberPadalka Andrew23-Feb-11 0:29 
GeneralRe: MemoryLeaks, imperfect container usage Pinmemberheadmyshoulder23-Feb-11 8:49 
Hi Andrew,
 
thank you for your valuable feedback.
 
You are right, in example1.cpp is a memory leak. It happens because the decorator_chain is constructed with wrong parameters. Try
decorator_chain< abstract_class > dc( true , true );
and the memory leaks are away. Maybe the defaults should be that way. I think this is the behaviour most people will assume.
 
Your version of the decorator_chain does not have all the features the original implementation had. Of course, the decorator_chain class could be implemented only one vector. But for me, my approach is a little bit more direct. One recognizes immediately the original instance. In your case, it is stored in m_decorators[0], which might be not obvious.
 
Why is there no need for _1 placeholders? could you give a small example?
 
Best regards,
 
headmyshoulder
GeneralRe: MemoryLeaks, imperfect container usage PinmemberAndrew Padalka23-Feb-11 21:56 
GeneralRe: MemoryLeaks, imperfect container usage Pinmemberheadmyshoulder23-Feb-11 22:09 
GeneralRe: MemoryLeaks, imperfect container usage PinmemberAndrew Padalka24-Feb-11 1:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411023.1 | Last Updated 22 Oct 2010
Article Copyright 2010 by headmyshoulder
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid