Composition vs. inheritance

T

Todd

Hello all,

While I have been programming for many years, I have decided that I
need to start thinking more like a developer than a programmer. As
such, I have been trying to improve my Java skills and OOAD skills.

One of the items I have run across in my self-education is the axiom
to "favor composition over inheritance" as it leads to more loosely
coupled designs. I am finding this difficult as I am a big user of
inheritance (I get the "is-a" relationship thing). I can see where I
could have a class that would have been a child-class have an object
of the parent class (assuming it is a concrete class) and then
delegate functionality to that object's methods. However, I don't
understand why now something that "is-a" is better off acting as if it
"has-a."

And beyond that, when is it then appropriate to inherit? It seems
that one would not want abstract classes any more because an object of
that class could be used for delegation. If one wanted to enforce
class "signatures" (help me with the terminology - I mean same method
names/signatures, etc.) one would only need interfaces. And yes, I
know that a class doesn't have to be abstract to be the root of an
inheritance hierarchy.

Thanks for your input,
Todd
 
D

Doug Morse

Todd,

Just FYI: Almost exactly the same issues / questions are being discussed right
now in comp.lang.python under the thread titled "Remove multiple inheritance
in Python 3000". I think that the 7-8 articles just posted there on this
nicely demonstrate the different viewpoints, recommendations, "wisdoms", etc.

HTH,
Doug
 
A

alexandre_paterson

Also, check out this link:

http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

It has been a while since I read it and I recall one of the examples
being bad, but other than that, I think it covers the topic nicely.

Yes that article is OK... However its title 'Why extends is evil' is
poorly choosed.

This is nicely explained here (by an ex- c.l.j.p. poster who
is now active in the Scala community):

http://pubs.tmorris.net/extends-not-evil/html/

"The Java extends keyword is not evil. It has never been evil;
"it will never be evil. What is evil, within a well defined axiom,
"is concrete behaviour inheritance, which is also known as
"'implementation inheritance' or just 'concrete inheritance'.
"Holub sets out to prove this, but inadvertently implies a
"correlation between the Java extends keyword and implementation
"inheritance. The fact is, this correlation does not exist.

The whole point being that 'extends' is *also* used when an
interface inherits from another interface.
 
C

Chase Preuninger

what if you want code in the abstract class but there is no sense in
creating one

look at the source of java.io_OutputStream
 
A

Arne Vajhøj

Todd said:
While I have been programming for many years, I have decided that I
need to start thinking more like a developer than a programmer. As
such, I have been trying to improve my Java skills and OOAD skills.

One of the items I have run across in my self-education is the axiom
to "favor composition over inheritance" as it leads to more loosely
coupled designs. I am finding this difficult as I am a big user of
inheritance (I get the "is-a" relationship thing). I can see where I
could have a class that would have been a child-class have an object
of the parent class (assuming it is a concrete class) and then
delegate functionality to that object's methods. However, I don't
understand why now something that "is-a" is better off acting as if it
"has-a."

And beyond that, when is it then appropriate to inherit? It seems
that one would not want abstract classes any more because an object of
that class could be used for delegation. If one wanted to enforce
class "signatures" (help me with the terminology - I mean same method
names/signatures, etc.) one would only need interfaces. And yes, I
know that a class doesn't have to be abstract to be the root of an
inheritance hierarchy.

The rule is well known.

Note the word "favor".

It does not mean that extend is always bad. It mean that extend is
often bad.

If the class is intended to be extended then it is OK to extend it.

A very good indication that the class is intended to be extended is if
it is abstract.

:)

So go ahead and extend abstract classes as you want.

Be very careful about extending non abstract classes. It is very
easy to make your class depend on some implementation detail
in the class it extends.

If there is a comment in the tops saying "extend this class to ...",
then it is probably OK to extend.

But if not then think twice.

Composition is a bit more work (your IDE should be able to
generate the delegate calls though) but safer.

Arne
 
U

Ulrich Eckhardt

Todd said:
One of the items I have run across in my self-education is the axiom
to "favor composition over inheritance" as it leads to more loosely
coupled designs.

Interesting that someone mentions a similar discussion but with Python as
background. In comp.lang.c++.moderated was a short thread "titled deriving
a class with a subset of base class methods?", which eventually turned into
the same direction.
I am finding this difficult as I am a big user of inheritance (I get
the "is-a" relationship thing). I can see where I could have a class
that would have been a child-class have an object of the parent class
(assuming it is a concrete class) and then delegate functionality to
that object's methods. However, I don't understand why now something
that "is-a" is better off acting as if it "has-a."

"is-a" and "has-a" are only ways to model things in programming, there are
grey areas where both could be used sensibly.
And beyond that, when is it then appropriate to inherit?

I can give you a few examples when it is not appropriate to inherit. One of
the things already started the discussion mentioned above, like e.g.
deriving a square class from a rectangle class. If you do that, you are
simply violating the Liskow(sp?) Substitution Principle, because inherently
expected behaviour of a square is incompatible to that of a rectangle. A
similar example is the relation between circle and ellipse. In both cases,
you have behaviour of one type (being able to set width/height separately)
which can't be combined with behaviour of the other type (width and height
are exactly the same).

Another example is deriving something from a file object like e.g. creating
a graphic file class. Typically this ends up with exposing the whole
baseclass interface (duh!) including the ability to write ASCII text to it
or similar things and further restricts the user to only files but not any
other stream type.

A further example is to derive from a string class which has neither a
protected nor any virtual interface, only to add one more member function.
Since this memberfunction must be implemented via the public interface of
its baseclass, you actually can't do anything that you couldn't have done
without inheritance in a plain old function. Java nicely allows a
programmer to declare their class as not suitable as baseclass in order to
prevent some of this nonsense.

In my eyes, the main use for inheritance is to customise the behaviour laid
out by the baseclass' interface, i.e. having the baseclass call your code.
If you merely extend the baseclass with new functions, it is practically
useless because none of these will ever be called by code that uses a
reference to the baseclass. For that code, you could also pass it a
reference to a contained baseclass object. Further, you would then have the
deriving/aggregating class' code and data better separated.

An example where derivation makes lots of sense is when reading XML
documents. You derive from an SAX class which gets fed a stream to parse.
It parses the stream and calls your code for the various elements.

BTW: Note that I'm talking about public inheritance here. Further, to be
honest, I don't even know if Java supports private inheritance.

cheers

Uli
 
T

Todd

I can give you a few examples when it is not appropriate to inherit. One of
the things already started the discussion mentioned above, like e.g.
deriving a square class from a rectangle class. If you do that, you are
simply violating the Liskow(sp?) Substitution Principle, because inherently
expected behaviour of a square is incompatible to that of a rectangle. A
similar example is the relation between circle and ellipse. In both cases,
you have behaviour of one type (being able to set width/height separately)
which can't be combined with behaviour of the other type (width and height
are exactly the same).


Uli,

The shape example that you give as to when it is _not_ appropriate to
use inheritance is exactly the example that was used in the courses I
have had in C++ and Java to show proper use of inheritance. So, if I
interpret this example correctly, as opposed to something of the sort:

abstract public class Rectangle
{
public Rectangle( double height, double width )
{
setHeight( height );
setWidth( width );
}

public void setHeight( double height )
{
this.height = height;
}

public void setWidth( double width )
{
this.width = width;
}

public double getHeight()
{
return height;
}

public double getWidth()
{
return width;
}


private double width;
private double height;
}

public class Square extends Rectangle
{
public Square( double side )
{
super( side, side );
}

public void setHeight( double height )
{
super.setHeight( height );
super.setWidth( height );
}

public void setWidth( double width )
{
super.setWidth( height );
super.setHeight( height );
}
}

which changes the behavior of setHeight and setWidth, you would favor
a Square somewhat like:

public class Square
{
public Square( double side )
{
square = new Rectangle( side, side );
}

public void setSide( double side )
{
square.setHeight( side );
square.setWidth( side );
}

public double getSide()
{
return square.getHeight();
}


private Rectangle square = null;
}

wherein the behavior of the Rectangle is hidden from user's of the
Square and methods with little/poor meaning are unavailable. (So in
this case, the Square is composed of a Rectangle, right?) This makes
sense since the behavior of the two items is inherently different,
however, I am still uncertain as to when to compose vs. extend when
the behavior is inherently the same.

For example, what if I wanted to ensure that MyRectangle always has a
longer width than height? The methods in an extended class will be
nearly identical to those of a composed class (with super replaced
with the composed object name):

public class MyRectangle extends Rectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

super( width, height );
}

public void setHeight( double height )
{
if( super.getWidth() < height )
{
super.setHeight( super.getWidth() );
super.setWidth( height );
}
else
{
super.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < super.getHeight() )
{
super.setWidth( super.getHeight() );
super.setHeight( width );
}
else
{
super.setWidth( width );
}
}
}

--- vs ---

public class MyRectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

myRectangle = new Rectangle( width, height );
}

public void setHeight( double height )
{
if( myRectangle.getWidth() < height )
{
myRectangle.setHeight( myRectangle.getWidth() );
myRectangle.setWidth( height );
}
else
{
myRectangle.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < myRectangle.getHeight() )
{
myRectangle.setWidth( myRectangle.getHeight() );
myRectangle.setHeight( width );
}
else
{
myRectangle.setWidth( width );
}
}


private Rectangle myRectangle = null;
}


I don't see how one relies less on the implementation of the Rectangle
class less than the other, since they both access the methods of the
Rectangle class.

Obviously, I am missing something fundamental, so I apologize if I
seem to be going in circles (no pun intended).

Todd

FYI, I didn't compile any of these examples, so please don't take them
as an SCCE.
 
T

Todd

That's decent advice for the case where one is dealing with someone
else's class, but the impression I got was that he's asking a more
general question, so that if he were designing both classes, he would
know when to design using inheritance and when to use composition.

Tim,

You are correct. I am trying to grasp proper design decision making
as opposed
to not having a choice when using an existing API. Thanks for
distilling that
point out for me.

Todd
 
T

Todd

Uli,

The shape example that you give as to when it is _not_ appropriate to
use inheritance is exactly the example that was used in the courses I
have had in C++ and Java to show proper use of inheritance. So, if I
interpret this example correctly, as opposed to something of the sort:

abstract public class Rectangle
{
public Rectangle( double height, double width )
{
setHeight( height );
setWidth( width );
}

public void setHeight( double height )
{
this.height = height;
}

public void setWidth( double width )
{
this.width = width;
}

public double getHeight()
{
return height;
}

public double getWidth()
{
return width;
}

private double width;
private double height;

}

public class Square extends Rectangle
{
public Square( double side )
{
super( side, side );
}

public void setHeight( double height )
{
super.setHeight( height );
super.setWidth( height );
}

public void setWidth( double width )
{
super.setWidth( height );
super.setHeight( height );
}

}

which changes the behavior of setHeight and setWidth, you would favor
a Square somewhat like:

public class Square
{
public Square( double side )
{
square = new Rectangle( side, side );
}

public void setSide( double side )
{
square.setHeight( side );
square.setWidth( side );
}

public double getSide()
{
return square.getHeight();
}

private Rectangle square = null;

}

wherein the behavior of the Rectangle is hidden from user's of the
Square and methods with little/poor meaning are unavailable. (So in
this case, the Square is composed of a Rectangle, right?) This makes
sense since the behavior of the two items is inherently different,
however, I am still uncertain as to when to compose vs. extend when
the behavior is inherently the same.

For example, what if I wanted to ensure that MyRectangle always has a
longer width than height? The methods in an extended class will be
nearly identical to those of a composed class (with super replaced
with the composed object name):

public class MyRectangle extends Rectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

super( width, height );
}

public void setHeight( double height )
{
if( super.getWidth() < height )
{
super.setHeight( super.getWidth() );
super.setWidth( height );
}
else
{
super.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < super.getHeight() )
{
super.setWidth( super.getHeight() );
super.setHeight( width );
}
else
{
super.setWidth( width );
}
}

}

--- vs ---

public class MyRectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

myRectangle = new Rectangle( width, height );
}

public void setHeight( double height )
{
if( myRectangle.getWidth() < height )
{
myRectangle.setHeight( myRectangle.getWidth() );
myRectangle.setWidth( height );
}
else
{
myRectangle.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < myRectangle.getHeight() )
{
myRectangle.setWidth( myRectangle.getHeight() );
myRectangle.setHeight( width );
}
else
{
myRectangle.setWidth( width );
}
}

private Rectangle myRectangle = null;

}

I don't see how one relies less on the implementation of the Rectangle
class less than the other, since they both access the methods of the
Rectangle class.

Obviously, I am missing something fundamental, so I apologize if I
seem to be going in circles (no pun intended).

Todd

FYI, I didn't compile any of these examples, so please don't take them
as an SCCE.

Sorry, noticed a couple of gotchas in my code posting:
1. With Rectangle abstract I wouldn't be able to instantiate
it in the latter composition examples.
2. "user's" should not have an apostrophe
 
T

Todd

Uli,

The shape example that you give as to when it is _not_ appropriate to
use inheritance is exactly the example that was used in the courses I
have had in C++ and Java to show proper use of inheritance. So, if I
interpret this example correctly, as opposed to something of the sort:

abstract public class Rectangle
{
public Rectangle( double height, double width )
{
setHeight( height );
setWidth( width );
}

public void setHeight( double height )
{
this.height = height;
}

public void setWidth( double width )
{
this.width = width;
}

public double getHeight()
{
return height;
}

public double getWidth()
{
return width;
}

private double width;
private double height;

}

public class Square extends Rectangle
{
public Square( double side )
{
super( side, side );
}

public void setHeight( double height )
{
super.setHeight( height );
super.setWidth( height );
}

public void setWidth( double width )
{
super.setWidth( height );
super.setHeight( height );
}

}

which changes the behavior of setHeight and setWidth, you would favor
a Square somewhat like:

public class Square
{
public Square( double side )
{
square = new Rectangle( side, side );
}

public void setSide( double side )
{
square.setHeight( side );
square.setWidth( side );
}

public double getSide()
{
return square.getHeight();
}

private Rectangle square = null;

}

wherein the behavior of the Rectangle is hidden from user's of the
Square and methods with little/poor meaning are unavailable. (So in
this case, the Square is composed of a Rectangle, right?) This makes
sense since the behavior of the two items is inherently different,
however, I am still uncertain as to when to compose vs. extend when
the behavior is inherently the same.

For example, what if I wanted to ensure that MyRectangle always has a
longer width than height? The methods in an extended class will be
nearly identical to those of a composed class (with super replaced
with the composed object name):

public class MyRectangle extends Rectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

super( width, height );
}

public void setHeight( double height )
{
if( super.getWidth() < height )
{
super.setHeight( super.getWidth() );
super.setWidth( height );
}
else
{
super.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < super.getHeight() )
{
super.setWidth( super.getHeight() );
super.setHeight( width );
}
else
{
super.setWidth( width );
}
}

}

--- vs ---

public class MyRectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

myRectangle = new Rectangle( width, height );
}

public void setHeight( double height )
{
if( myRectangle.getWidth() < height )
{
myRectangle.setHeight( myRectangle.getWidth() );
myRectangle.setWidth( height );
}
else
{
myRectangle.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < myRectangle.getHeight() )
{
myRectangle.setWidth( myRectangle.getHeight() );
myRectangle.setHeight( width );
}
else
{
myRectangle.setWidth( width );
}
}

private Rectangle myRectangle = null;

}

I don't see how one relies less on the implementation of the Rectangle
class less than the other, since they both access the methods of the
Rectangle class.

Obviously, I am missing something fundamental, so I apologize if I
seem to be going in circles (no pun intended).

Todd

FYI, I didn't compile any of these examples, so please don't take them
as an SCCE.

Sorry, noticed a couple of gotchas in my code posting:
1. With Rectangle abstract I wouldn't be able to instantiate
it in the latter composition examples.
2. "user's" should not have an apostrophe
 
T

Todd

Uli,

The shape example that you give as to when it is _not_ appropriate to
use inheritance is exactly the example that was used in the courses I
have had in C++ and Java to show proper use of inheritance. So, if I
interpret this example correctly, as opposed to something of the sort:

abstract public class Rectangle
{
public Rectangle( double height, double width )
{
setHeight( height );
setWidth( width );
}

public void setHeight( double height )
{
this.height = height;
}

public void setWidth( double width )
{
this.width = width;
}

public double getHeight()
{
return height;
}

public double getWidth()
{
return width;
}

private double width;
private double height;

}

public class Square extends Rectangle
{
public Square( double side )
{
super( side, side );
}

public void setHeight( double height )
{
super.setHeight( height );
super.setWidth( height );
}

public void setWidth( double width )
{
super.setWidth( height );
super.setHeight( height );
}

}

which changes the behavior of setHeight and setWidth, you would favor
a Square somewhat like:

public class Square
{
public Square( double side )
{
square = new Rectangle( side, side );
}

public void setSide( double side )
{
square.setHeight( side );
square.setWidth( side );
}

public double getSide()
{
return square.getHeight();
}

private Rectangle square = null;

}

wherein the behavior of the Rectangle is hidden from user's of the
Square and methods with little/poor meaning are unavailable. (So in
this case, the Square is composed of a Rectangle, right?) This makes
sense since the behavior of the two items is inherently different,
however, I am still uncertain as to when to compose vs. extend when
the behavior is inherently the same.

For example, what if I wanted to ensure that MyRectangle always has a
longer width than height? The methods in an extended class will be
nearly identical to those of a composed class (with super replaced
with the composed object name):

public class MyRectangle extends Rectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

super( width, height );
}

public void setHeight( double height )
{
if( super.getWidth() < height )
{
super.setHeight( super.getWidth() );
super.setWidth( height );
}
else
{
super.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < super.getHeight() )
{
super.setWidth( super.getHeight() );
super.setHeight( width );
}
else
{
super.setWidth( width );
}
}

}

--- vs ---

public class MyRectangle
{
public MyRectangle( double width, double height )
{
if( width < height )
{
double temp = width;
width = height;
height = temp;
}

myRectangle = new Rectangle( width, height );
}

public void setHeight( double height )
{
if( myRectangle.getWidth() < height )
{
myRectangle.setHeight( myRectangle.getWidth() );
myRectangle.setWidth( height );
}
else
{
myRectangle.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < myRectangle.getHeight() )
{
myRectangle.setWidth( myRectangle.getHeight() );
myRectangle.setHeight( width );
}
else
{
myRectangle.setWidth( width );
}
}

private Rectangle myRectangle = null;

}

I don't see how one relies less on the implementation of the Rectangle
class less than the other, since they both access the methods of the
Rectangle class.

Obviously, I am missing something fundamental, so I apologize if I
seem to be going in circles (no pun intended).

Todd

FYI, I didn't compile any of these examples, so please don't take them
as an SCCE.

Sorry, noticed a couple of gotchas in my code posting:
1. With Rectangle abstract I wouldn't be able to instantiate
it in the latter composition examples.
2. "user's" should not have an apostrophe
 
S

Stefan Ram

Todd said:
Obviously, I am missing something fundamental, so I apologize if I
seem to be going in circles (no pun intended).

When you answer to a post, you should only quote
the parts you directly refer to. Google Groups
is not a good newsreader.

The whole rectangle square discussion only stems
from insufficiant care to distinguish between
value and storage objects.

Let Q be the set of »quarternary digits« {0,1,2,3},
its subset {0,1} is called B; B is »the set of
binary digits«. The inclusion

B c Q (B is a subset of Q)

is valid.

A quartary storage q* can store a quartary digit. It
also might be used to store a binary digit. However, one
can not use any binary storage to store any quartenary digit.

So, for the set of binary storages B* and the set of
quartary storages Q*:

Q* c B* (Q* is a subset of B*)

In general, if every B value is a Q value, then every Q storage
is a B storage.

If one now does not care to distinguish between values and
memories, this would be worded as »If every B is a Q, then
every Q is a B«, which is false.

The rectangle square problem only exists as long as one does
not make it clear whether one wants to model rectangle and
square /values/ or rectangle and square /storages/.
 
A

Arne Vajhøj

Tim said:
That's decent advice for the case where one is dealing with someone
else's class, but the impression I got was that he's asking a more
general question, so that if he were designing both classes, he would
know when to design using inheritance and when to use composition.

Then it becomes a little bit more blurred. But I still think the
general ideas hold.

If you have an "is a" relation ship and the parent class can be
written as an abstract class clearly intended to be extended, then
that is a fine design.

If not default to composition.

Obviously writing the parent class yourself do give some
freedom to tweak it either way. But if it is designed
with a view on future usage, then the problem domain can
often direct what is the best fit.

Arne
 
A

alexandre_paterson

A very good indication that the class is intended to be extended is if
it is abstract.

:)

So go ahead and extend abstract classes as you want.

OK, but the OP's question could also be read as 'should I design
my own programs/framework' by creating abstract classes?

It's not because the OP is sometimes forced to use badly crafted
frameworks forcing him to extend abstract classes that abstract
classes are a good thing.

In my opinion it's a fundamentally broken design.

As I already pointed out both James Gosling and Bjarne Stroustrup
agree on that one, as do, say, the Spring developers, users, etc.

http://www.artima.com/intv/gosling34.html

(this is really an eye opener).

My other post seems to be lost, so here's Stroustrup's take on this:

http://www.artima.com/intv/modern.html

Stroustrup:

"Since then I have consistently pointed out that one of the major
"ways of writing classes in C++ is without any state, that is,
"just an interface.

Actually they do agree on more then that: they even agree on the
fact that 'concrete inheritance', a.k.a 'implementation inheritance'
is fundamentally broken.

There are even OO languages that explicitely forbids implementation
inheritance (and other that mandates it but that is another story).

And these guys knows way better than me, as do the Spring developer.

If there is a comment in the tops saying "extend this class to ...",
then it is probably OK to extend.

Yup... But to me it's also a warning saying: "be very careful when
using this framework, it's probably not been designed by OO gurus".
(I'd rather keep using frameworks like Spring that do the Right Thing
[TM]).
 
U

Ulrich Eckhardt

Todd said:
Uli,

The shape example that you give as to when it is _not_ appropriate to
use inheritance is exactly the example that was used in the courses I
have had in C++ and Java to show proper use of inheritance. So, if I
interpret this example correctly, as opposed to something of the sort:

abstract public class Rectangle
{
public Rectangle( double height, double width )
{
setHeight( height );
setWidth( width );
}

public void setHeight( double height )
{
this.height = height;
}

public void setWidth( double width )
{
this.width = width;
}

public double getHeight()
{
return height;
}

public double getWidth()
{
return width;
}


private double width;
private double height;
}

Okay. Let's sum up what I would expect if I only knew the interface of this
class:
1. Being able to read back values:
rect.setWidth(2);
if(rect.getWidth() != 2)
error();
2. Independently setting values:
rect.setWidth(2);
rect.setHeight(42);
if(rect.getWidth() != 2)
error();
public class Square extends Rectangle
{
public Square( double side )
{
super( side, side );
}

public void setHeight( double height )
{
super.setHeight( height );
super.setWidth( height );
}

public void setWidth( double width )
{
super.setWidth( height );
super.setHeight( height );
}
}

Yes. Here. the confusing part is that the second example above doesn't work
any more. Further, the reason for that is non-obvious: I have a rectangle,
set its width and height and they are then not as I set them!
which changes the behavior of setHeight and setWidth, you would favor
a Square somewhat like:

public class Square
{
public Square( double side )
{
square = new Rectangle( side, side );
}

public void setSide( double side )
{
square.setHeight( side );
square.setWidth( side );
}

public double getSide()
{
return square.getHeight();
}


private Rectangle square = null;
}

Well, I'm not sure if I would use a class Rectangle in order to implement
the square but in general, I agree with the behaviour and interface.
wherein the behavior of the Rectangle is hidden from user's of the
Square and methods with little/poor meaning are unavailable. (So in
this case, the Square is composed of a Rectangle, right?) This makes
sense since the behavior of the two items is inherently different,
however, I am still uncertain as to when to compose vs. extend when
the behavior is inherently the same.

For example, what if I wanted to ensure that MyRectangle always has a
longer width than height? The methods in an extended class will be
nearly identical to those of a composed class (with super replaced
with the composed object name):

public class MyRectangle extends Rectangle
{
public MyRectangle( double width, double height )
{ [...] }

public void setHeight( double height )
{
if( super.getWidth() < height )
{
super.setHeight( super.getWidth() );
super.setWidth( height );
}
else
{
super.setHeight( height );
}
}

public void setWidth( double width)
{
if( width < super.getHeight() )
{
super.setWidth( super.getHeight() );
super.setHeight( width );
}
else
{
super.setWidth( width );
}
}
}

Again, the same confusing behaviour ensues. It's hard to envision the case
where you would need this, but just like with the case of a square, I would
consider creating a separate interface. Otherwise, I would consider only
using class Rectangle and wherever some code places additional restraints
on it either fix 'invalid' rects or throw an error.
public class MyRectangle
{ [ similar implementation but using aggregation ] }
I don't see how one relies less on the implementation of the Rectangle
class less than the other, since they both access the methods of the
Rectangle class.

The important point is that one version claims to be a Rectangle while the
other doesn't. Even though the aggregate version uses a Rectangle, a use
will only look at the public interface of that class in order to understand
how to use it. When deriving from Rectangle, the user will look at
MyRectangle's interface and its baseclass' or maybe only at the baseclass'
interface (because they might only have a reference to a Rectangle).

Obviously, I am missing something fundamental, so I apologize if I
seem to be going in circles (no pun intended).

I don't think I can give you a final answer to this. Much of this requires
feeling and experience in order to determine which one is right. I wouldn't
even call something like this 'right', it is just a question of what is
better for the program, ease of understanding, extending, maintenance,
testability, scalability etc. It's not black and white, it's grey.
FYI, I didn't compile any of these examples, so please don't take them
as an SCCE.

No problem. ;)

Uli
 
J

Joshua Cranmer

I've slowly come to the view that the famous "is-a" test is rather flawed.
The problem lies in that "is-a" is a question of _type_ -- an emu is a bird,
an array is a list, a BigInt is an integer, while what's inherited is
_behaviour_. An emu _is_ a bird, but it doesn't _behave_ like a bird; an
array _is_ a list, but it doesn't _behave_ like a list; a BigInt _is_ an
integer, but it doesn't _behave_ like an integer, a circle _is_ an ellipse,
but it doesn't _behave_ like an ellipse and so on and on.

In /Effective C++/, Scott Meyers wrote of the "is-a" test:

"... the instincts you've developed in other fields of study--including
mathematics--may not serve you as well as you expect."

As you clearly observe, specialization is not grounds for inheritance
(Mr. Meyers refers to the classic Rectangle/Square example). In fact, in
this very newsgroup, there was once a discussion on whether or not a
mathematical Vector should inherit from a Matrix (the decision was no,
for obvious reasons).

Nor does biology present a good model for inheritance: the
classification hierarchy from kingdom to species tend to be based on
genetic divergence as much as features such as ability to fly. Too many
special cases there.

The largest problem is probably the term "is-a;" to be and to have are
among some of the loosest verbs in terms of definition. "is-a", AFAICT,
is mostly used to differentiate between "has-a" in terms of composition.
Therefore, it works fine as a test between composition and inheritance,
but it is a poor indicator of when inheritance is justified and correct.
As you mention, inheritance is only justified when the interface is
inherited.

Actually, circles do behave like ellipses in all fashions, except that a
circle is an ellipse where the major and minor axises are fixed to the
same length (along with other facts that may imply). It falls under the
"specialization" rule, however.
In other words, just because something is a more specialised type of
something else, it's not necessarily useful to use inheritance between them.
(And, the other side of the coin, even when something is _not_ a subtype of
something, it _could_ be a good idea to inherit behaviour -- a flying
squirrel isn't a bird, but it might be useful to inherit bird's fly
behaviour.)

I wish textbooks would stop describing inheritance in terms of
biological examples. About the only thing that is good for is
illustrating where inheritance is a poor choice.
 
A

Arne Vajhøj

I've slowly come to the view that the famous "is-a" test is rather flawed.
The problem lies in that "is-a" is a question of _type_ -- an emu is a bird,
an array is a list, a BigInt is an integer, while what's inherited is
_behaviour_. An emu _is_ a bird, but it doesn't _behave_ like a bird; an
array _is_ a list, but it doesn't _behave_ like a list; a BigInt _is_ an
integer, but it doesn't _behave_ like an integer, a circle _is_ an ellipse,
but it doesn't _behave_ like an ellipse and so on and on.

In other words, just because something is a more specialised type of
something else, it's not necessarily useful to use inheritance between them.
(And, the other side of the coin, even when something is _not_ a subtype of
something, it _could_ be a good idea to inherit behaviour -- a flying
squirrel isn't a bird, but it might be useful to inherit bird's fly
behaviour.)

An emu is a bird and it behaves like a bird. If you have put
functionality in bird that an emu does not have then you have put
functionality in bird that does not belong there. That is design
error not a problem with is-a.

An array is not a list (in the java.util.List sense).

A BigInteger is not an Integer (use Java definitions).

Circle-ellipsis could be a problem. I think that OO and math
may have slightly different definitions of is-a.

Is-a is type, but type in OO means behavior.

And having flying squirrel inherit bird is a very good example
of when not to use inheritance.

Arne
 
P

Patricia Shanahan

Arne Vajhøj wrote:
....
Circle-ellipsis could be a problem. I think that OO and math
may have slightly different definitions of is-a.
....

Isn't this just a matter of the immutable vs. mutable issue?

Each individual circle is an ellipse, and has exactly the behavior of an
ellipse whose major axis and minor axis are both equal to the radius of
the circle. There is a problem if you allow an ellipse to change
dimensions while it exists, but is that really a necessary feature of an
Ellipse class?

Patricia
 
S

Stefan Ram

Patricia Shanahan said:
Isn't this just a matter of the immutable vs. mutable issue?

Yes.

(The relation to mathematics is, that in mathematics, there is
no concept of time and no concept of storage and nothing
changes, so one can not argue about mutability in mathematics.)

I have posted this some days ago, and repeat it here:

quotation:

The whole rectangle square discussion only stems
from insufficiant care to distinguish between
value and storage objects.

Let Q be the set of »quarternary digits« {0,1,2,3},
its subset {0,1} is called B; B is »the set of
binary digits«. The inclusion

B c Q (B is a subset of Q)

is valid.

A quartary storage q* can store a quartary digit. It
also might be used to store a binary digit. However, one
can not use any binary storage to store any quartenary digit.

So, for the set of binary storages B* and the set of
quartary storages Q*:

Q* c B* (Q* is a subset of B*)

In general, if every B value is a Q value, then every Q storage
is a B storage.

If one now does not care to distinguish between values and
memories, this would be worded as »If every B is a Q, then
every Q is a B«, which is false.

The rectangle square problem only exists as long as one does
not make it clear whether one wants to model rectangle and
square /values/ or rectangle and square /storages/.

end of quotation
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top