Is creating a base class for all applications of a particular type good design?
I am trying to write a graphics application in C++. It currently uses OGRE for display, but I'd like it to work with Irrlicht or any other engine, even a custom rendering engine which supports my needs. This is a rather long question, so I'd appreciate help on re-tagging/ cleanup (if necessary). I'll start with a little background.
The application has three major states:
1. Display rasterized scene 2. Display a ray traced version of the same scene 3. Display a hybrid version of the sceneClearly, I can divide my application into four major parts:
1. A state management system to switch between the above modes. 2. An input system that can receive both keyboard and mouse input. 3. The raster engine used for display. 4. The ray tracing system.Any application encompassing the above needs to be able to:
1. Create a window. 2. Do all the steps needed to allow rendering in that window. 3. Initialize the input system. 4. Initialize the state manager. 5. Start looping (and rendering!).I want to be able to change the rendering engine/state manager/input system/ ray tracing system at any time, so long as certain minimum requirements are met. Imho, this requires separating the interface from the implementation. With that in mind, I created the interfaces for the above systems.
At that point, I noticed that the application has a common 'interface' as well. So I thought to abstract it out into an ApplicationBase class with virtual methods. A specific application, such as one which uses OGRE for window creation, rendering etc would derive from this class and implement it.
My first question is - is it a good idea to design like this?
Here is the code for the base class:
#ifndef APPLICATION_H
#define APPLICATION_H
namespace Hybrid
{
//Forward declarations
class StateManager;
class InputSystem;
//Base Class for all my apps using hybrid rendering.
class Application
{
private:
StateManager* state_manager;
InputSystem* input_system;
public:
Application()
{
try
{
//Create the state manager
initialise_state_manager();
//Create the input system
initialise_input_system();
}
catch(...) //Change this later
{
//Throw another exception
}
}
~Application()
{
delete state_manager;
delete input_system;
}
//If one of these fails, it throws an
//exception.
virtual void initialise_state_manager() = 0;
virtual void initialise_input_system() = 0;
virtual void create_window() = 0;
//Other methods.
};
#endif
When I use OGRE, I rely on OGRE to create the window. This requires OGRE to be initialised before the createWindow() function is called in my derived class. Of course, as it is, createWindow is going to be called first! That leaves me with the following options:
1. Leave the base class constructor empty. 2. In the derived class implementation, make initialising OGRE开发者_Python百科 part of the createWindow function. 3. Add an initialize render system pure virtual function to my base class. This runs the risk of forcing a dummy implementation in derived classes which have no use for such a method.My second question is- what are your recommendations on the choice of one of these strategies for initialising OGRE?
You are mixing two unrelated functions in one class here. First, it serves as a syntactic shortcut for declaring and initializing StateManager and InputSystem members. Second, it declares abstract create_window function.
If you think there should be a common interface - write an interface (pure abstract class).
Additionally, write something like OgreManager self-contained class with initialization (looping etc) methods and event callbacks. Since applications could create and initialize this object at any moment, your second question is solved automatically.
Your design may save a few lines of code for creating new application objects, but the price is maintaining soup-like master object with potentially long inheritance line.
Use interfaces and callbacks.
P.S.: not to mention that calling virtual functions in constructor doesn't mean what you probably expect.
Yes, that is a good design, and it is one that I use myself.
For your second question, I would remove anything from the base constructor that has any possibility of not being applicable to a derived class. If OGRE wants to create the window itself then you need to allow it to do that, and I don't think that it makes sense to initialize OGRE in createWindow (it's misleading).
You could add an initialize render system virtual method, but I think you should just leave that task to the derived class's constructor. Application initialization is always a tricky task, and really, really difficult to abstract. From my experience, it's best not to make any assumptions about what the derived class might want to do, and just let it do the work itself in any way that it wants.
That said, if you can think of something that will absolutely apply to any conceivable derived class then feel free to add that to the base constructor.
精彩评论