Search

Sponsored Links

Meta

Categories

Archives

Recent Posts

RSS Feeds

27
Feb

Composition Over Inheritance

Related Blog Items

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

creative commonsThis work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.

Popularity: 7%

You need to log on to convert this article into PDF


Related Blog Items

No Comments

No comments yet.

Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-spam image