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!