composition vs inheritance

spir denis.spir at gmail.com
Wed Mar 16 05:01:46 PDT 2011


composition vs inheritance

I have carefully read the document "Prefer composition to inheritance", from 
C++ coding standards, at
http://www.artima.com/cppsource/codestandards3.html, by Herb Sutter and Andrei 
Alexandrescu.

In general, I do agree in that composition makes for a simpler scheme, and more 
flexible. But when writing D code, I constantly step on the same issue that 
without inheritance and (runtime type) polymorphism, I simply cannot express 
/very/ common things.
For instance, say I have 3 kinds of Xs X1 X2 X3. I would like to write:

void moveTogether (X[] xs, Vector vector) {
     foreach (x ; xs) {
         lookAround(x);
         x.move(vector);

         if (isX3(x)) {
             writeln(x.motto);
             x.jump();
         }
     }
}

void lookAround (X x) {...}

Let us forget the if clause for now. There are several issue. First, I cannot 
put elements of the 3 types into an X[] collection. Second, a function that 
expects an X like lookAround will not accept elements of types  X1 X2 X3. 
Third, moveTogether will call move on X (if any) instead of the proper move on 
each element's type.
How do you solve this without inheritance and polymorphism? How do you solve 
this with D structs?

There is also an issue with inheritance and method dispatch (reason why I wrote 
"(if any)" above): for x.move() to work, move must be defined on X even if it 
does not make any sense.
This is were the if clause enters the game: some subtype(s) may have additional 
data or function members that really should not be defined on the super type. 
(Think at different kinds of nodes in a tree, eg a parse tree). To be able to 
access them in a 'generic' func like moveTogether, I need to define fake 
members on X.
This is really ugly, misleading, and costly. Then, I could as well define a 
single, haevy, kind of X with all the stuff for every subtype. But doing so I 
would lose the ability to specialise given members like move...

Go solves this, I guess, with its very nice notion of interface. In this case, 
there may be a Mobile interface with a single method move. X1 X2 X3 
automatically satisfy it by having move defined (there is no need for explicite 
"implements" or inheritance). Thus, function signatures become:

void moveTogether (Mobile[] ms, Vector vector)
void lookAround (Mobile m)

...and everbody's happy (I guess).

Note: Such interfaces would also nicely replace all the ugly stuff with is() 
for template preconditions:

     void f (Mobile M) (M m) {...}
instead of:
     void f (M) (M m) if (is(somethingIDontWantToWrite)) {...}

Actually (unsure), I think with interfaces we don't even need a generic func / 
template here.


Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d mailing list