Re: Composition vs. Inheritance (Was: colored point redux)

Daniel Yoo <[email protected]> Mon, 26 Feb 2007 10:35:44 -0500 (EST)
Newsgroups gmane.comp.lang.lightweight
Message-ID <[email protected]>
> Or, another example, strings and linked lists are both sequences, and 
> thus could inherit some behavior (methods) that are common to all 
> sequences. How would this be accomplished by composition?


Hi Robbert,

Let's assume that we have some described behavior and we want to reuse 
that behavior without retyping it.

Let's do a concrete example: let's say we do have things that support 
something "getitem"ish, and we'd like to provide different ways to iterate 
over them.  We can provide some template class for iteration.  I'll use 
Java for these examples just to be very concrete.

/**********************************************************/
import java.util.Iterator;

public abstract class ForwardIteration {
     // Returns the ith item
     abstract Object getItem(int i);

     // Returns the length
     abstract int len();

     public Iterator iter() {
 	class MyIter implements Iterator {
 	    int k = 0;
 	    public Object next() {
 		return getItem(k++);
 	    }

 	    public boolean hasNext() {
 		return k < len();
 	    }
 	    public void remove() {
 		throw new UnsupportedOperationException();
 	    }
 	}
 	return new MyIter();
     }
}
/**********************************************************/

For the sake of the example, here's a quick-and-dirty silly thing to fill 
out the template.

/**********************************************************/
import java.util.Iterator;
public class MyList extends ForwardIteration {
     private String[] items = {"hello", "world"};

     public Object getItem(int i) {
 	return items[i];
     }

     public int len() {
 	return 2;
     }

     static public void main(String[] args) {
 	for (Iterator iter = new MyList().iter(); iter.hasNext() ; ) {
 	    System.out.println(iter.next());
 	}
     }
}
/**********************************************************/


Ok, that works.  One problem, though, is allowing these things to support 
_multiple_ ways of iteration.  How do we use inheritance to support both 
forward iteration and backward iteration?  I'd like to be able to write 
BackwardIteration:

/**********************************************************/
import java.util.Iterator;

public abstract class BackwardIteration {
     // Returns the ith item
     abstract Object getItem(int i);

     // Returns the length
     abstract int len();

     public Iterator iter() {
         class MyIter implements Iterator {
             int k = len() - 1;
             public Object next() {
                 return getItem(k--);
             }

             public boolean hasNext() {
                 return k >= 0;
             }
             public void remove() {
                 throw new UnsupportedOperationException();
             }
         }
         return new MyIter();
     }
}
/**********************************************************/

But now MyList can't inherit from both ForwardIteration and 
BackwardIteration, if we live from a single-inheritance restriction.

And even if we could, we've still got the Highlander problem: in the end, 
there can be only one.  They conflict with the same method name iter(). 
So that's why vanilla inheritence fails as a mechanism for reuse here.


In contrast, we can do what we want with object composition:

/************************************************************/
import java.util.Iterator;

interface Sequence {
     Object getItem(int i);
     int len();
}

class ForwardIteration2 {
     public Iterator iter(final Sequence seq) {
 	class MyIter implements Iterator {
 	    int k = 0;
 	    public Object next() {
 		return seq.getItem(k++);
 	    }

 	    public boolean hasNext() {
 		return k < seq.len();
 	    }
 	    public void remove() {
 		throw new UnsupportedOperationException();
 	    }
 	}
 	return new MyIter();
     }
}
/************************************************************/

Now writing an equivalent BackwardIteration2 using object composition 
should be no problem, and using both Iterations on a sequence is not an 
issue.


Another difficulty with inheritance as to get reuse is that "extending" 
our classes requires us to edit our old source code to push those 
behaviors into the old code.  This might not be possible under certain 
situations, like not having a component's source code available to us, for 
example.

So those are some of the problems with trying to use inheritence as a 
mechanism for reuse in Java.  The issue is that it doesn't quite compose 
very well, compared to object composition.  I'm not sure how you plan to 
handle this problem in your own language, but it's something to keep in 
mind.

PLT Scheme's doing something with "traits" to avoid this issue:

     http://www.cs.utah.edu/plt/publications/aplas06-fff.pdf



Best of wishes!