Inheritance guidelines.

S

superheathen

Hi,

While I think I have a reasonable grasp of C++ in general, I've never
really used it more than a convenient C. as such, I'm finding very
basic OO confusing, not so much the concept, but how everything should
be laid out in practice.

One of those is deciding whether or not a class should be inherited
or declared as an object in the class.

note: this isn't isn't a specific problem, just making pseudo code as
an example.

class RootProgramClass : public ConfigClass, public Window
{
}

vs.

class RootProgramClass
{
ConfigClass config;
Window window;
}

How about appropriate use of constructors? something tells me a
constructor taking arguments just to pass onto an inherited classes
constructor, to pass onto the final classes constructor, to initialize
the classes variables, isn't very good design. Are there ways to avoid
this mess and allow the programmer to initialize an double inherited
classes variables neatly.

these are all newbish questions and it shames me to ask them,
unfortunately I'm nothing more than a hobbyist so don't have the
benefit of university level courses, etc.

any resources on good OO design, etc. would be appreciated.
 
V

Victor Bazarov

While I think I have a reasonable grasp of C++ in general, I've never
really used it more than a convenient C. as such, I'm finding very
basic OO confusing, not so much the concept, but how everything should
be laid out in practice.

One of those is deciding whether or not a class should be inherited
or declared as an object in the class.

note: this isn't isn't a specific problem, just making pseudo code as
an example.

class RootProgramClass : public ConfigClass, public Window
{
}

vs.

class RootProgramClass
{
ConfigClass config;
Window window;
}

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.

Or, you have some kind of extended configuiration class that doesn't
just keep your configuration (and overrides some generic ConfigClass
behaviour), but also provides additional elements, like storing the
configuration settings somewhere special (like Internet). Then you
basically extend your ConfigClass, and the CustomConfigurator "is-a"
ConfigClass.

Now, speaking of your concrete example... Considering the names of
the classes, I'd probably go for the latter.

What is the purpose/functionality of 'RootProgramClass'? While you
didn't specify it, if I just suppose that it's like a singleton that
keeps some important things while your program is running, if it's
like what is often called "the Application object", then the program
"has-a" config and "has-a" window, but it isn't any of those things.

As you can see "is-a" and "has-a" are two fundamental types of entity
relationships in a design. NewsgroupReader "is-a" InternetUser, and
FtpUploaderDownloader "is-a" InternetUser. They share some common
traits and functionality, probably. ElectricCar "has-a" motor, and
at the same time ElectricCar "is-a" vehicle...
How about appropriate use of constructors?
Huh?

something tells me a
constructor taking arguments just to pass onto an inherited classes
constructor, to pass onto the final classes constructor, to initialize
the classes variables, isn't very good design. Are there ways to avoid
this mess and allow the programmer to initialize an double inherited
classes variables neatly.

Methinks you're approaching it from the wrong side. Don't design
the software or class hierarchy from the POV of using constructors.
Design from the POV of entity relationships. Design from the POV
of interfaces. Essentially, you need to say, "who and how is going
to use my class[es]?" Then you will have the initial layout of your
model represntation. Then you take a look at the implementation of
that first layout, and probably figure out the other pieces of the
framework (every solution has some kind of framework, even if it
doesn't name it that). Then you just keep going through the design
iterations until you have it finalized. By that time you will
probably have it half-implemented.
these are all newbish questions and it shames me to ask them,
unfortunately I'm nothing more than a hobbyist so don't have the
benefit of university level courses, etc.

any resources on good OO design, etc. would be appreciated.

Try 'comp.object', OOD is their very topic.

V
 
J

Juha Nieminen

Victor said:
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.
 
A

Alf P. Steinbach

* Juha Nieminen:
[About circles and ellipses]
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.

Generally, when that's the case, the person's concepts are flawed :).

E.g., a constant circle Is-A constant ellipse (the general relationship
is a bit more complicated).

In C++ this can be expressed via interfaces or via implicit conversion.


[snip]
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.

Sorry, but there's no general "the" correct solution.


Cheers, & hth.,

- Alf
 
S

superheathen

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.

Yeah, the is-a has-a relationships seem blatantly obvious in some
examples, such as Dog : Animal
Civic : Car, a car isn't an engine, but it has one, so Car should
contain engine rather than inherit from it. Sometimes it just doesn't
seem so clear to me though. I'm starting to think though that my
problem isn't lack of understanding in regards to composition vs.
inheritance, which seems pretty straightforward, but the areas in
which I'm programming.

For a better example than the one I originally provided consider this:
You have a Game class, an Engine class, and a Window class.

The Window class is responsible for say, resizing, OpenGL
initialization, and updating the window. The Engine class is
responsible for monitoring input and dispatches events to virtual
functions which are overridden in the Game class and tied to game
logic, like updating position of objects, etc. The only way I can
think of making this work is Game inheriting Engine inheriting
Window. We come back to is-a and has-a.

Should Engine have a Window? or should Engine be an extension of
Window? Should a Game class have an Engine? or should a Game class
been an extension of an Engine?

extension of _x is the only way I could conceive of it working, but it
doesn't quite seem right. Perhaps I should be asking these questions
on a game development group or forum. ;-)
 
V

Victor Bazarov

[..] I'm starting to think though that my
problem isn't lack of understanding in regards to composition vs.
inheritance, which seems pretty straightforward, but the areas in
which I'm programming.

It seems that once the relationship has been established, you can
tell whether it's logical or not, but when you have a set of your
own types/concepts, figuring out the relationship between them is
a challenge.
For a better example than the one I originally provided consider this:
You have a Game class, an Engine class, and a Window class.

Is "Model/View/Controller" pattern something you'd be intersted in?
The pattern is very common and many books have been written around
it.
The Window class is responsible for say, resizing, OpenGL
initialization, and updating the window. The Engine class is
responsible for monitoring input and dispatches events to virtual
functions which are overridden in the Game class and tied to game
logic, like updating position of objects, etc. The only way I can
think of making this work is Game inheriting Engine inheriting
Window. We come back to is-a and has-a.

Should Engine have a Window? or should Engine be an extension of
Window? Should a Game class have an Engine? or should a Game class
been an extension of an Engine?

Start by reading/thinking about MVC layout of your program. What
is the view? A window. What does it do? What is the role of the
view in that architecture? What is playing the controller? What
is the model?

The main question you need to ask (and answer) is "what does it
do", about each of the pieces in your application. Try not to
start with "I have an Engine, a Game, and a Window -- who owns
what and who is what?" Those are the wrong questions to ask.
Why do you have an Engine? Well, you probably need one (or more)
to make your application do something, but is it (or are they)
really the top-level element(s)?

Design from bottom up is difficult if not wrong.
extension of _x is the only way I could conceive of it working, but it
doesn't quite seem right. Perhaps I should be asking these questions
on a game development group or forum. ;-)

The more venues you visit, the more information you'll collect,
and that's A GOOD THING(tm). Of course, developing a game is
not really different from developing a CAD system or a Banking
system. All have data, all have UI. All have components you
get from 3rd parties (engines, libraries). But don't start
with "I have a brick and a board, what house do I build from
those?" Start by asking what house you want, what should it
have, and then arrive to bricks and boards you may need to
accomplish your goals.

V
 
D

Dave Rahardja

Hi,

While I think I have a reasonable grasp of C++ in general, I've never
really used it more than a convenient C. as such, I'm finding very
basic OO confusing, not so much the concept, but how everything should
be laid out in practice.

One of those is deciding whether or not a class should be inherited
or declared as an object in the class.

I find that the Liskov Substitution Principle (GIYF) is a useful and
concrete way to determine if public inheritance is appropriate. Stated
simply, the LSP requires that a publicly derived class be substitutible
for any instance of the base class.

In other words, if class D publicly derives from class B, then
everywhere you have a B* pointing to a B object, you can substitute a D
object with no change to the algorithm semantics.

In practical terms, this may mean that if you have a unit test for B,
you must be able to pass that same unit test with D.


Here are some other guidelines:

For "is-a" relationships, use public inheritance.
For "has-a" relationships, use composition (member variables).
For "is-implemented-in-terms-of" relationships, use private inheritance.

Never, never, ever use protected inheritance.

-dr
 
J

Juha Nieminen

Alf said:
* Juha Nieminen:
[About circles and ellipses] 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.

Generally, when that's the case, the person's concepts are flawed :).

E.g., a constant circle Is-A constant ellipse (the general relationship
is a bit more complicated).

IMO that's the wrong usage of the "is-a" relationship. IMO "is-a" is
more related to abstract/concrete than to human language. Even though
you can *say* "thing A is a thing B", that doesn't mean that B is a more
abstract concept than A. IMO the "is-a" relation should always be
applied with levels of abstraction.

An ellipse is not a more abstract concept than a circle. They are both
equally concrete concepts. (And in fact, in a way, one could even argue
that an ellipse is slightly more concrete than a circle because the
ellipse has more information in it than a circle.)

I would say that from OO point of view a circle is *not* an ellipse,
even if in mathematics you could say that it is. However, OOD is a
different field from mathematics.
In C++ this can be expressed via interfaces or via implicit conversion.

Even if you can, that doesn't mean you should.
Sorry, but there's no general "the" correct solution.

You are welcome to suggest a better solution from OOD point of view.
 
J

Juha Nieminen

The Window class is responsible for say, resizing, OpenGL
initialization, and updating the window. The Engine class is
responsible for monitoring input and dispatches events to virtual
functions which are overridden in the Game class and tied to game
logic, like updating position of objects, etc. The only way I can
think of making this work is Game inheriting Engine inheriting
Window. We come back to is-a and has-a.

Actually that sounds to me like a clear case where you should use a
callback mechanism.

Your game engine class is not a window, it's an event listener. Thus
it should be inherited from an event listener interface/class. When you
instantiate your Window class, you give a pointer/reference to your game
engine (which "is-a" event listener). The Window instance just sees a
pointer/reference of type event listener and calls its virtual functions
when events happen.

(There are advantages in doing it like this. For example, you can
change the event listener which the Window instance dispatches the
events to on the fly. You could even have more than one event listener
which receive events from the same Window instance.)
 
A

Alf P. Steinbach

* Juha Nieminen:
Alf said:
* Juha Nieminen:
[About circles and ellipses] 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.
Generally, when that's the case, the person's concepts are flawed :).

E.g., a constant circle Is-A constant ellipse (the general relationship
is a bit more complicated).

IMO that's the wrong usage of the "is-a" relationship. IMO "is-a" is
more related to abstract/concrete than to human language. Even though
you can *say* "thing A is a thing B", that doesn't mean that B is a more
abstract concept than A. IMO the "is-a" relation should always be
applied with levels of abstraction.

An ellipse is not a more abstract concept than a circle. They are both
equally concrete concepts. (And in fact, in a way, one could even argue
that an ellipse is slightly more concrete than a circle because the
ellipse has more information in it than a circle.)

I would say that from OO point of view a circle is *not* an ellipse,
even if in mathematics you could say that it is. However, OOD is a
different field from mathematics.

I think you didn't read what you replied to.

Anyway, I would just have to write the same again.

If you don't understand, then you don't, but you could try (one good way
is to actually "do", i.e. designing and implementing this).


Even if you can, that doesn't mean you should.


You are welcome to suggest a better solution from OOD point of view.

I have.


Cheers, & hth.,

- Alf
 

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,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top