Является ли создание базового класса для всех приложений определенного типа хорошим дизайном? - PullRequest
4 голосов
/ 02 февраля 2010

Я пытаюсь написать графическое приложение на C ++. В настоящее время он использует OGRE для отображения, но я бы хотел, чтобы он работал с Irrlicht или любым другим движком, даже с пользовательским механизмом рендеринга, который поддерживает мои потребности. Это довольно длинный вопрос, поэтому я буду признателен за помощь в повторной пометке / очистке (при необходимости). Начну с небольшого фона.

Приложение имеет три основных состояния:
1. Отображение растрированной сцены
2. Отображение версии трассированной трассы той же сцены
3. Показать гибридную версию сцены

Очевидно, я могу разделить свое приложение на четыре основные части:
1. Система управления состоянием для переключения между вышеуказанными режимами.
2. Система ввода, которая может принимать ввод с клавиатуры и мыши.
3. Растровый движок, используемый для отображения.
4. Система трассировки лучей.

Любое приложение, охватывающее вышеперечисленное, должно иметь возможность:
1. Создайте окно.
2. Выполните все шаги, необходимые для разрешения рендеринга в этом окне.
3. Инициализируйте систему ввода.
4. Инициализируйте менеджер состояния.
5. Начните цикл (и рендеринг!).

Я хочу иметь возможность изменять механизм рендеринга / менеджер состояний / систему ввода / систему трассировки лучей в любое время, при условии соблюдения определенных минимальных требований. Имхо, это требует отделения интерфейса от реализации. Имея это в виду, я создал интерфейсы для вышеуказанных систем.

В этот момент я заметил, что приложение также имеет общий «интерфейс». Поэтому я решил абстрагировать его в класс 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

Когда я использую OGRE, я полагаюсь на OGRE, чтобы создать окно. Это требует инициализации OGRE перед вызовом функции createWindow () в моем производном классе. Конечно, как и прежде, createWindow будет вызываться первым! Это оставляет меня со следующими параметрами:
1. Оставьте конструктор базового класса пустым.
2. В реализации производного класса сделайте инициализацию OGRE частью функции createWindow.
3. Добавить инициализирующую систему визуализации чисто виртуальную функцию в мой базовый класс. Это создает риск принудительной реализации в производных классах, которые не используют такой метод.

Мой второй вопрос: каковы ваши рекомендации по выбору одной из этих стратегий для инициализации OGRE?

Ответы [ 2 ]

3 голосов
/ 02 февраля 2010

Вы смешиваете две несвязанные функции в одном классе. Во-первых, он служит синтаксическим ярлыком для объявления и инициализации членов StateManager и InputSystem. Во-вторых, объявляется абстрактная функция create_window.

Если вы считаете, что должен быть общий интерфейс - напишите интерфейс (чистый абстрактный класс).

Кроме того, напишите что-то вроде автономного класса OgreManager с методами инициализации (зацикливания и т. Д.) И обратными вызовами событий. Поскольку приложения могут создавать и инициализировать этот объект в любой момент, ваш второй вопрос решается автоматически.

Ваш дизайн может сохранить несколько строк кода для создания новых объектов приложения, но цена поддерживает ведущий объект, похожий на суп, с потенциально длинной строкой наследования.

Использовать интерфейсы и обратные вызовы.

P.S .: не говоря уже о том, что вызов виртуальных функций в конструкторе не означает то, что вы, вероятно, ожидаете.

2 голосов
/ 02 февраля 2010

Да, это хороший дизайн, и я использую его сам.

Что касается вашего второго вопроса, я бы удалил из базового конструктора что-либо, что может быть неприменимо к производному классу. Если OGRE хочет создать само окно, вам нужно разрешить ему это сделать, и я не думаю, что имеет смысл инициализировать OGRE в createWindow (это вводит в заблуждение).

Вы можете добавить виртуальный метод инициализации системы визуализации, но я думаю, что вы должны просто оставить эту задачу конструктору производного класса. Инициализация приложения - это всегда сложная задача, которую действительно трудно абстрагировать. Исходя из моего опыта, лучше не делать каких-либо предположений о том, что может захотеть делать производный класс, а просто позволить ему выполнять работу самостоятельно любым удобным для него способом.

Тем не менее, если вы можете придумать что-то, что будет абсолютно применимо к любому мыслимому производному классу, не стесняйтесь добавлять это к базовому конструктору.

...