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

Reply via email to