Re: Inheritance guidelines.
Victor Bazarov wrote:
The only reason for public inheritance is to extend the class from
which you inherit. For example, you have a generic Window that does
not really do much, only knows how to report its own position, say,
and has a bunch of virtual functions to respond to some system
"messages", then any window you create will probably be descending
from that type... So, according to very early teachings on OOD,
the relationship between your CustomWindow and a generic Windows
exists and it's the "is-a" relationship. CustomWindow "is-a" Window.
Another basic concept of object-oriented design (which can have
exceptions, but is still a good rule-of-thumb) is that a base class is a
more abstract concept than a derived class, which is a more concrete
concept.
The classic example is a class hierarchy related to living organisms.
For example, you can have a class named "Animal" and a class derived
from it called "Dog".
"Animal" is a more abstract concept: It defines the properties which
are common to all animals. "Dog" is a more concrete concept: It defines
all the same properties as "Animal", but besides them it defines
additional properties which are exclusive to dogs. It's thus a more
concrete definition at a conceptual level.
You could also have a class named "Cat", which is derived from
"Animal". It defines a different type of animal, with its own properties.
This nicely follows the "is-a" relationship: A "Dog" is an "Animal", a
"Cat" is an "Animal", but a "Dog" is not a "Cat". They are distinct
classes. A function taking an "Animal" can be given a "Dog" or a "Cat",
but a function taking a "Dog" cannot be given a "Cat".
This also answer the classic OOD problem: If we have circles and
ellipses, should circle be inherited from ellipse, or the other way around.
Maybe circle should be inherited from an ellipse? After all, a circle
*is* an ellipse, just with a constraint. It's a special type of ellipse.
However, if we do that, we encounter problems: For example, an ellipse
has two radiuses, but a circle has only one. Thus a circle does *not*
implement all the properties of an ellipse.
So maybe the ellipse should be inherited from a circle? After all, an
ellipse can be thought as an extension of a circle. However, this also
causes problems: If a function taking a circle asks it for its radius,
what should the ellipse return? The major radius? The minor one? Some
combination? Not good.
The answer is that neither should be inherited from the other. This is
because a circle and an ellipse are at the *same* conceptual level. They
are both drawing primitives. You can't really say that one concept is
more abstract than the other. They are both equally concrete concepts.
Thus inheriting one from the other would be wrong.
The correct solution is to create a more abstract concept which is
common to both circles and ellipses (and other similar drawing
primitives), for example a "DrawingPrimitive" class, and inherit
"Circle" and "Ellipse" from it. Thus a circle *is a* drawing primitive,
and an ellipse *is a* drawing primitive.