Several Patterns books will always say that composition is preferred over inheritance, because it makes your code more reusable, and more flexible. Let’s see a usual example of this in C++, using pointers or references:
class my_object {
private:
other_object * _p;
public:
explicit my_object (other_object * p) : _p(p) { };
void do_something() { _p->do_something() };
};
This allows the user to define the functionality of my_object’s do_something method during runtime by passing in a pointer to an object that’s of type other_object or an object that derives from (or inherits from) other_object.
Sounds like a disaster because in C and C++, you can cast a pointer to anything into a pointer of another type. Let’s see how someone might be able to try doing this:
int b;
my_object a((other_object *)&b);
Disaster! And not to mention, bad coding style… We can go about it by using a reference instead of a pointer to avoid this abuse:
class my_object {
private:
other_object & _r;
public:
explicit my_object (other_object & r) : _r(r) { };
void do_something () { _r.do_something() };
};
This is still dangerous because someone can actually do something like this:
int b;
other_object * p = (other_object *) &b;
my_object ( *p );
Which will compile. What we want really, is to enforce that the implementation of do_something is passed on to a concrete implementation that actually *does something* (pardon the pun). How do we achieve that to prevent mis-use? We use templates:
template <typename>
class my_object {
private:
do_trait _r;
public:
explicit my_object ( ) : _r() { };
void do_something () { _r.do_something() };
};
NOW, with this, the code will only compile if and only if the type defined for do_trait has a default constructor, and a method that implements do_something(). We then restrict the use of the object during compile time, such that we don’t need to pass the implementation during runtime — which is usually where the problems occur.
The only way we can use the object then, is to provide a conforming type for do_trait when we want an instance of my_object that has a do_something that behaves accordingly.
A sample of the use of the class above, is the following:
my_object<other_object> a;
my_object<my_other_object> b;
We can even make it a bit funkier this way:
struct my_object {
};
template <typename>
class my_object_templ : my_object {
private:
do_trait _r;
public:
explicit my_object_templ ( ) : _r() { };
void do_something () { _r.do_something() };
};
//... somewhere in the code...
std::auto_ptr<my_object> a(new my_object_templ<other_type>() );
a.reset(new my_object_templ<my_other_type>() );
But then this still leads us to the same problems as before — though now, we have a re-usable composite implementation, which has a common interface to do_something(). That way, we can re-use this implementation wherever we might need a place where an object implements a do_something() method.
Makes sense? Questions and comments welcome. :)
by Dean Michael
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.
Popularity: 6%