0

I'm creating my own drawing library in C++ to provide shared rendering code for my projects.

Since the library is designed to be used as a component of other projects, the renderer's representation of 3D objects and scenes will be "attached" to whatever the program's native representation of an "object" or "scene" is. Then, the renderer will use this scene information to draw all the objects.

There are two approaches I could go with. The first approach would involve assigning each drawable object in my program (for this example, let's call our program's objects ProgramObject) a Drawable member from the drawing library. Then, whenever a ProgramObject is created, it gets added to a list in an instance of the program's "scene" class (let's call this one ProgramScene), which can then add the ProgramObject's Drawable member to it's DrawableScene member. The ProgramScene can register its DrawableScene with the renderer for drawing. A Drawable contains information such as mesh data, materials, etc. The renderer consumes Drawables from a DrawableScene and uses them to construct a frame in OpenGL.

The second approach would be mostly the same, except instead of ProgramObject and ProgramScene having Drawable and DrawableScene members, ProgramObject and ProgramScene would inherit from Drawable and DrawableScene respectively.

Which approach would be better?

0

1 Answer 1

3

When in doubt, it is usually better to prefer composition over inheritance. Still, that is just a heuristic, not a law, and asking if this heuristic applies to your specific case is definitely justified.

Here is a simple argument why I think you should prefer composition in this case: if you want to keep your design open to make Drawable a base class of certain more specific "drawables" inside your library (like 2D or 3D drawables, or OpenGL vs. DirectX drawables, or some other kind of technical classification), use composition. That will allow it to let the technical inheritance hierarchy stay orthogonal with any kind of domain specific inheritance hierarchy outside your library, where ProgramObject may be the base class of certain domain specific classes (like a player character, or a vehicle, or some kind of other object).

This article here calls these aspects different dimensions:

When you have a situation where either composition or inheritance will work, consider splitting the design discussion in two:

  • The representation/implementation of your domain concepts is one dimension
  • The semantics of your domain concepts and their relationship to one another is a second dimension

In general, inheriting within one of these dimensions is fine. The problem becomes when we forget to separate the two dimensions, and start inheriting across inter-dimensional boundaries.

And this is exactly what would happen when you start to inherit a ProgramObject from a Drawable: you would mix up the "drawing" dimension with domain concepts ("dimensions"), which can easily lead to a very unflexible design, where one will run into trouble when trying to separate technical and domain specific concerns.

Let me add that there scenarios where inheriting from Drawable in an application context can make sense - when the library is designed to allow specific drawables (seen as extensions in the technical dimension) in an application. But I guess you would not call this subclass a ProgramObject anymore.

3
  • Thanks for the answer! This makes a lot of sense. I'll definitely be trying out this approach. Maybe, though, I could still have classes like ProgramScene inherit from DrawableScene?
    – Bunabyte
    Commented Sep 16 at 22:29
  • @AcinonX: I doubt that gives you a benefit. It will effectively prevent your program scene two have two different kind of drawing scenes, for example. I think the dimensional argument of not mixing technical and domain concerns in an inheritance hierarchy applies here as well.
    – Doc Brown
    Commented Sep 17 at 1:36
  • ... Of course, when someone wants a ProgramScene to be something like an extended DrawingScene , then maybe. Your lib could support both kinds of usage, for example an ExtendedDrawingScene which is used by a ProgramScene. But it makes no sense to decide this without more context.
    – Doc Brown
    Commented Sep 17 at 1:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.