www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - composition vs inheritance

reply spir <denis.spir gmail.com> writes:
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
Mar 16 2011
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 16 Mar 2011 08:01:46 -0400, spir <denis.spir gmail.com> wrote:

 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) {...}

From the article: "Of course, these are not arguments against inheritance per se. Inheritance affords a great deal of power, including substitutability and/or the ability to override virtual functions." i.e., use inheritance because your design requires it. -Steve
Mar 16 2011
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/16/2011 07:01 AM, spir wrote:
 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.

You are exaggerating the frequency of certain situations and needs.
 For instance, say I have 3 kinds of Xs X1 X2 X3.

That's not such a common encounter. There are many conditions that the three kinds must satisfy - such as they must have a lot in common and very few differences. Also, all differences must have been thought over at X's creation time and put in the interface of X. THEN you are supposed to use an inheritance hierarchy. This is less frequent than people initially thought. In the 1980s it was believed that everything would belong in a hierarchy and the only challenge was to find them "good" hierarchies. Now it is quite clear things aren't all that simple. Andrei
Mar 16 2011