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; decorator d1( &c ); decorator d2( &d1 ); decorator d3( &d2 ); d3.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