Re: Composition vs. inheritance
On Apr 23, 1:24 am, Ulrich Eckhardt <eckha...@satorlaser.com> wrote:
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.