Should i use multiple inheritance in this scenario?

Started by
5 comments, last by SuperVGA 10 years, 10 months ago
Hi guys,
I'm still working on my engine/framework currently sporting GUI and Scenegraphs and I've never had
to use multiple inheritance for any of this (Or I have been able to justify its use). Just yesterday, I bumped into a problem.
I have the following classes:
  • SceneElement (A node in my scenegraph. Contains transformation vectors as well as dimensions and BBs for placeholder rendering. Based on Renderable, as SceneElements can be rendered)
  • OffscreenRenderer (Basically an FBO wrapper. It's unrelated to SceneElement purpose-wise but is itself a Renderable, and contains a list of 0..* Renderables that will be drawn offscreen. It owns a Projection, and vectors for Position, Orientation and "Up". It also has a getter for the texture name, and a couple of private vars related to FBOs.
The "Renderable" difference between the two is that SceneElement is often a visual thing that can be shown, and OffscreenRenderer is something that will have stuff drawn onto it, while not self being visible. I was thinking earlier, to have both Renderable and a Renderer base classes,
but I discarded the idea as it would seem too redundant, and they'd both end up with a render() function anyways.
Now, I actually discovered the issue because, as I was creating a Camera class, inheriting from both of the above,
i now had two variables called orientation (the position alias in SceneElement is called offset, but that still smells either way.)
I also notice that Renderable goes for both of the base classes, and Camera as such has two of each Renderable variable!
It's two grandmothers is the same single person. Yuck!
When I based Camera upon SceneElement, I did it for the sole purpose of being able to parse the camera into a scene. But the camera itself is never shown, so it's different from other renderables. It's an invisible point entity,
and I'm really not sure I should keep it where it is.
I like the idea of my display queue to have a Camera as one of its renderables, and then afterwards, when the GUI is rendered, my View element now tied to the (OffscreenRenderer)Camera will show whatever was rendered for the Camera.
Should I aggregate OffscreenRenderer onto Camera instead?
Sorry about the wall of text. I'd like to illustrate it, but I don't want to lock the concepts down too much, as I am, obviously, reconsidering it.
Keep an open mind. :)
Advertisement

I honestly don't understand the motivation for having Camera be a SceneElement/Renderable. You said yourself it is never shown, so it's unlike other Renderables. So... it shouldn't be a Renderable.

As for OffscreenRenderer... I would think that would "have-a" Camera, not "be-a" Camera.

Camera shouldn't inherit from either of those classes.

I honestly don't understand the motivation for having Camera be a SceneElement/Renderable. You said yourself it is never shown, so it's unlike other Renderables. So... it shouldn't be a Renderable.

Thanks, I think I could go with my Renderable and Renderer approach instead, then? Just to clearly indicate to myself that there are (here) two different applications to rendering something. Or i could call it canvas and drawable, just to lower the confusion.

As for OffscreenRenderer... I would think that would "have-a" Camera, not "be-a" Camera.

I was thinking whethere Camera could be-an OffscreenRenderer.

But it can certainly also have it...

Inheritance represents a perfect "is a" relationship. It is the among the tightest coupling you can give things in the C++ language.

The principle has been formalized in computer science, is known as the Liskov Substitution Principle, or LSP.

Never use public inheritance to implement "almost IS-A."

There is also an exception for a pure abstract interface. Languages like Java and C# use this very effectively. In that case you are not inheriting functionality, you are inheriting an interface protocol.

So, a camera may implement a SceneElement interface, that can make sense. If SceneElement were an interface that might make sense. But the way you have described it above it is an object and not an interface, so the camera will HAVE a SceneElement.

The same thing with an OffscreenRenderer. It might have an interface that could be implemented, or it might have a component, but It is not perfectly subsitutable. The relationship is "almost is a".


OffscreenRenderer (Basically an FBO wrapper. It's unrelated to SceneElement purpose-wise but is itself a Renderable, and contains a list of 0..* Renderables that will be drawn offscreen.

Seems to be the same problem. It contains things, but LSP does not apply so it should not use inheritance. Use containment.

Generally inheritance trees should be shallow and wide. For example, a GameObject class that contains all the functionality used by all game objects, and several hundred subclasses that only override one or two functions to very slightly modify behavior.

You are describing inheritances tree that are deep and narrow. For example, Camera is a OffscreenRenderer is an FBO Wrapper is a Renderable. In this type of situation use an interface or containment, but not regular inheritance.

Inheritance represents a perfect "is a" relationship. It is the among the tightest coupling you can give things in the C++ language.

The principle has been formalized in computer science, is known as the Liskov Substitution Principle, or LSP.

Never use public inheritance to implement "almost IS-A."

There is also an exception for a pure abstract interface. Languages like Java and C# use this very effectively. In that case you are not inheriting functionality, you are inheriting an interface protocol.

So, a camera may implement a SceneElement interface, that can make sense. If SceneElement were an interface that might make sense. But the way you have described it above it is an object and not an interface, so the camera will HAVE a SceneElement.

The same thing with an OffscreenRenderer. It might have an interface that could be implemented, or it might have a component, but It is not perfectly subsitutable. The relationship is "almost is a".


OffscreenRenderer (Basically an FBO wrapper. It's unrelated to SceneElement purpose-wise but is itself a Renderable, and contains a list of 0..* Renderables that will be drawn offscreen.

Seems to be the same problem. It contains things, but LSP does not apply so it should not use inheritance. Use containment.

Generally inheritance trees should be shallow and wide. For example, a GameObject class that contains all the functionality used by all game objects, and several hundred subclasses that only override one or two functions to very slightly modify behavior.

You are describing inheritances tree that are deep and narrow. For example, Camera is a OffscreenRenderer is an FBO Wrapper is a Renderable. In this type of situation use an interface or containment, but not regular inheritance.

Frob, thanks for all the good input.

I would never have posted if I felt everything was fine about this design bit. In general, I don't have that many base classes, and only a few levels. Yes,

the camera is special. I've completely coupled it away from the Scene stuff. But i can see that it IS an OffscreenRenderer. It's the only thing that makes sense here,

but now I might split things up so that i have Renderer and Renderable, inheriting OffscreenRenderer from the former.

Camera is a OffscreenRenderer is a Renderer is as deep, perhaps deeper than I feel comfortable going. (I don't have an actual FBO wrapper class, it's just what the OffscreenRenderer does)

I wrote that it contains a list of Renderables. These are just pointers. I'd never let the Camera contain what it's viewing, only how it's doing it.

I have used interfaces for my Interaction class, enabling key_up() and such to be run on the object. Other than that I don't have many interfaces.

Thanks again guys, if you have some more good suggestions, feel free to add them. I think I'll be able to continue down a more solid path now.

Wait... why is Camera an OffscreenRenderer?

From what you described, your OffscreenRenderer has a list of things to render, right? So I'm assuming it needs a camera to do so. Thus, it doesn't make sense for Camera to inherit from OffscreenRenderer. OffscreenRenderer should instead contain a Camera (or possibly it should be told to render its objects using such-and-such camera).

Maybe your idea of a camera is different than mine. When I think of camera, I think of basically a class that calculates and exposes the View and Projection matrices, and provides ways to manipulate those via setting the camera position, lookat and up vector. It should not know anything about renderables or scenes or anything.

Wait... why is Camera an OffscreenRenderer?

From what you described, your OffscreenRenderer has a list of things to render, right? So I'm assuming it needs a camera to do so. Thus, it doesn't make sense for Camera to inherit from OffscreenRenderer. OffscreenRenderer should instead contain a Camera (or possibly it should be told to render its objects using such-and-such camera).

Maybe your idea of a camera is different than mine. When I think of camera, I think of basically a class that calculates and exposes the View and Projection matrices, and provides ways to manipulate those via setting the camera position, lookat and up vector. It should not know anything about renderables or scenes or anything.

Thanks for your continued interest.

I wanted the Camera to be an abstraction. Closer to the scripting part and generally easier to recognize as a user of my framework.

An OffscreenRenderer does everything related to rendering to a texture. (Only a page or two, but it's nice to keep it separate.)

It also has position and directions, so that you won't have to pass that to it all the time. (An offscreenrenderer renders time-sliced every x frames, and in mixed resolutions)

On the top of OffscreenRenderer i have Camera, which I see basically as a lens (projection settings) and a human-readable identifier on the top of that.

I could have EnvironmentMap with 6 OffscreenRenderers (Now aggregation, - i see where this is going!)

Or Minimap, with an orthagonal projection.

I don't want the camera to be directly responsible for what it needs to render, I want a different part of my project taking care of geography and scenes to do that to the OffscreenRenderer. Only issue wtih that is that I'd like to place a Camera into a Scene context using these Human Readable Ids, and OffscreenRenderer needs to know.

So I guess you're right with regards to OffscreenRenderer and aggregation (although we conclude this in slightly different ways).

Maybe Camera could implement a "Renderer" interface, requiring it to state how many OffscreenRenderers it has() and the ability to get() one and render() everything?

It's always great with some more inputs. I can also clearly see your point with regards to what the Camera should do. I only wanted to mask away some of the underlying behavior, as I wouldn't have to worry about that when implementing Camera-like stuff.

This topic is closed to new replies.

Advertisement