I have the following problem in application architecture and am willing to solve it (sorry for a lot of text).
I am building a game engine prototype and I have base abstract class AbstractRenderer
(I will use C++ syntax, but still the problem is general).
Assume there are some derived implementations of this renderer, let's say DirectxRenderer
and OpenglRenderer
.
Now, let's say that only one of these renderers (let's stick to DirectX-based) has a member called IDirect3D9Device* m_device;
Obviously at this point everything is fine - m_device
is used internally in DirectxRenderer
and shouldn't be exposed in the abstract AbstractRenderer
superclass.
I also add some abstract rendering interface, for instance IRenderable
. It means simply one pure virtual method virtual void Render(AbstractRenderer* renderer) const = 0;
And this is the place where some problems start. Assume I am modelling some scene, so, this scene will probably have some geometrical objects in it.
I create abstract superclass AbstractGeometricalObject
and derived DirectX-based implementation DirectxGeometricalObject
. The second one would be responsible for storing pointers to DirectX-specific vertex & index buffers.
Now - the problem.
AbstractGeometricalObject
should obviously derive the IRenderable
interface, because it's renderable in logical terms.
If I derive my DirectxGeometricalObject
from AbstractGeometricalObject
, the first one should have virtual void Render(AbstractRenderer* renderer) const { ... }
method in it, and that Abstract...
stuff brings some troubles.
See the code for better explanation:
And for now my classes look the following way:
class AbstractGeometricalObject : public IRenderable {
virtual void Render(AbstractRenderer* renderer) const { ... }
};
class DirectxGeometricalObject : public AbstractGeometricalObject {
virtual void Render(AbstractRenderer* renderer) const {
// I think it's ok to assume that in 99 / 100 cases the renderer
// would be a valid DirectxRenderer object
// Assume that rendering a DirectxGeometricalObject requires
// the renderer to be a DirectxRenderer, but not an AbstractRenderer
// (it could utilize some DX-specific settings, class members, etc
// This means that I would have to ***downcast*** here and this seems really
// bad to me, because it means that this architecture sucks
renderer = dynamic_cast<DirectxRenderer*>(renderer);
// Use the DirectX capabilities, that's can't be taken out
// to the AbstractRenderer superclass
renderer.DirectxSpecificFoo(...);
}
I know I'm probably worrying too much, but this downcast in such a simple case means that I could be forced to make lots of downcasts if my application grows.
Definitely, I would like to avoid this, so please, could you advice me something better in design terms / point out my errors.
Thank you
This might be a situation where the template pattern (not to be confused with C++ templates) comes in handy. The public Render
in the abstract class should be non-virtual, but have it call a private virtual function (e.g. DoRender). Then in the derived classes, you override DoRender instead.
Here's an article that goes into great depth describing the use of template pattern with private virtual functions.
Edit:
I started to put together an example of what I meant, and it seems like there's actually a broader flaw in the architecture. Your use of AbstractRenderer is somewhat frivolous since you're forcing each geometricalobject to be intimately aware of a particular renderer type.
Either the renderer should be able to work off the public methods of Renderables, or Renderables should be able to work off the public methods of the Renderer. Or perhaps you can give the concrete renderers a Renderable factory if there really needs to be such an intimate connection. I'm sure there are some other patterns that would fit well, too.