代码之家  ›  专栏  ›  技术社区  ›  batbrat

为特定类型的所有应用程序创建一个基类是好的设计吗?

  •  4
  • batbrat  · 技术社区  · 15 年前

    我试着在C++中编写一个图形应用程序。它目前使用ogre进行显示,但我希望它与irrlicht或任何其他引擎一起工作,甚至是一个支持我需要的自定义渲染引擎。这是一个相当长的问题,所以我希望在重新标记/清理(如有必要)方面得到帮助。我先从一些背景开始。

    该应用程序有三种主要状态:
    1.显示光栅化的场景
    2。显示同一场景的光线跟踪版本
    三。显示场景的混合版本

    很明显,我可以将我的申请分为四个主要部分:
    1。在上述模式之间切换的状态管理系统。
    2。一种能同时接收键盘和鼠标输入的输入系统。
    三。用于显示的光栅引擎。
    4。光线跟踪系统。

    包含上述内容的任何应用程序都必须能够:
    1。创建一个窗口。
    2。执行在该窗口中允许渲染所需的所有步骤。
    三。初始化输入系统。
    4。初始化状态管理器。
    5。开始循环(和渲染!).

    我希望能够随时更改渲染引擎/状态管理器/输入系统/光线跟踪系统,只要满足某些最低要求。imho,这需要将接口与实现分离。考虑到这一点,我为上述系统创建了接口。

    在这一点上,我注意到应用程序也有一个公共的“接口”。所以我想用虚拟方法把它抽象成一个applicationbase类。一个特定的应用程序,例如使用ogre创建窗口、渲染窗口等的应用程序,将从这个类派生并实现它。

    我的第一个问题是——这样设计是个好主意吗?

    下面是基类的代码:

    #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
    

    当我使用食人魔时,我依靠食人魔来创建窗口。这要求在派生类中调用CreateWindow()函数之前初始化Ogre。当然,如它所示,CreateWindow将首先被调用!这给我留下了以下选择:
    1。将基类构造函数保留为空。
    2。在派生类实现中,使初始化ogre成为createWindow函数的一部分。
    三。向我的基类添加一个初始化呈现系统纯虚拟函数。这就有可能在派生类中强制使用一个虚拟实现,而派生类对这种方法没有用处。

    我的第二个问题是-您对选择其中一种初始化Ogre的策略有什么建议?

    2 回复  |  直到 15 年前
        1
  •  3
  •   ima    15 年前

    您在这里的一个类中混合了两个不相关的函数。首先,它充当声明和初始化StateManager和InputSystem成员的语法快捷方式。其次,它声明抽象的create_window函数。

    如果您认为应该有一个公共接口-编写一个接口(纯抽象类)。

    此外,使用初始化(循环等)方法和事件回调编写类似于OgreManager的自包含类。由于应用程序可以随时创建和初始化这个对象,所以第二个问题会自动解决。

    您的设计可能会为创建新的应用程序对象节省几行代码,但代价是使用可能较长的继承行来维护类似于soup的主对象。

    使用接口和回调。

    P.S.:更不用说,在构造函数中调用虚拟函数并不意味着您可能期望的结果。

        2
  •  2
  •   Peter Alexander    15 年前

    是的,这是一个很好的设计,也是我自己用的。

    对于您的第二个问题,我将从基本构造函数中删除任何可能不适用于派生类的内容。如果Ogre想要创建窗口本身,那么您需要允许它这样做,我认为在CreateWindow中初始化Ogre是没有意义的(这有误导性)。

    您可以添加一个初始化呈现系统虚拟方法,但我认为您应该将该任务留给派生类的构造函数。应用程序初始化总是一项棘手的任务,而且非常难以抽象。根据我的经验,最好不要对派生类可能要做的事情做任何假设,并且让它以它想要的任何方式自己完成工作。

    也就是说,如果您能想到一些绝对适用于任何可以想到的派生类的东西,那么可以随意地将其添加到基本构造函数中。