Я разрабатываю несколько классов для моего проекта в C++
на данный момент но у меня проблема.
Я хочу создать camera
класс, который содержит все необходимые значения (например, матрицы преобразования), но функцию, которая отображает camera
должен быть обменяемым. Это звучит как обычный случай для паттерна стратегии. Таким образом, я создал интерфейс, который определяет render-function
и дал camera
класс pointer
к этому интерфейсу.
Проблема в том, что render function
нужен доступ ко всем данным в camera
класс, и поэтому я дал этой функции указатель на camera
класс в качестве параметра. Это выглядит так:
#include "ICameraRender.h"
class Camera{
private:
ICameraRender* _cameraRender;
public:
Camera();
Camera(ICameraRender& cameraRender);
~Camera();
void renderCamera(){ _cameraRender->render(this); }void setCameraRender(ICameraRender& cameraRender);
/..../
};class ICameraRender{
public:
virtual ~ICameraRender(){
}
//Override me
virtual void render(Camera* camera) = 0;
};
Это не кажется элегантным решением из-за ответственности за цикл бесконечности (вызов camera->renderCamera()
в render-function
в ICameraRender
). Есть ли лучшее решение этой проблемы?
С уважением
РЕДАКТИРОВАТЬ:
Я придумал другое решение. Поскольку функция, которая работает с данными камеры, требует только доступа к данным, я думал, что смогу разделить сам класс камеры. Класс называется Camera и CameraModel. Последний содержит все необходимые данные, а первый выполняет операции с ним.
Поэтому мне просто нужно передать указатель на CameraModel в мою функцию:
class CameraModel{
private:
/...data.../
public:
/...setter and getter.../
};
class Camera{
private:
CameraModel* _cameraModel;
ICameraRender* _cameraRender;
public:
Camera();
Camera(ICameraRender& cameraRender);
~Camera();
void renderCamera(){ _cameraRender->render(_cameraModel); }
void setCameraRender(ICameraRender& cameraRender);
/..../
};
class ICameraRender{
public:
virtual ~ICameraRender(){
}
//Override me
virtual void render(CameraModel* cameraModel) = 0;
};
Теперь функция render (которая только вычисляет новые значения для камеры в соответствии с пользовательским вводом) больше не имеет доступа к функции renderCamera.
Что вы думаете об этом решении?
С уважением, Стэн
Вы правы, это похоже на плохой дизайн. 🙂
Я не понимаю, почему для рендеринга камеры нужен доступ к камере. Я уверен, что вы можете передать что-то еще в качестве параметра. Для рендеринга не требуется доступ ко всем элементам камеры, поэтому вы можете просто передать те, которые им нужны (и, если их много, обернуть их в структуру CameraConfig
или что-то типа того).
Если разным рендерам нужны разные параметры, вы можете создать отдельную иерархию с ICameraConfig
,
Вероятно, сейчас самое время использовать дизайн на основе политик для реализации шаблона стратегии, особенно если вы используете C ++ и, вероятно, нацеливаетесь на компилятор старше 2002 года. (Поскольку механизм шаблонирования в C ++ настолько хорош, мы можем получить шаблон стратегии бесплатно таким образом!)
Первое: заставьте ваш класс принять класс стратегии / политики (в данном случае ваш ICameraRenderer) с параметром шаблона. Затем укажите, что вы используете определенный метод из этого параметра шаблона. Сделайте вызовы этого метода в классе камеры …
Затем реализуйте свои стратегии как обычный старый класс с методом render ()!
Это будет выглядеть примерно так:
class Camera<RenderStrategy>{
using RenderStrategy::render;
/// bla bla bla
public:
void renderCamera(){ render(cameraModel); }
};
class SpiffyRender{
public:
void render(CameraModel orWhateverTheParameterIs){ // some implementation goes somewhere }
};
Всякий раз, когда вы хотите сделать камеру, которая использует одну из этих политик / стратегий:
// the syntax will be a bit different, my C++ chops are rusty;
// in general: you'll construct a camera object, passing in the strategy to the template parameter
auto SpiffyCamera = new Camera<SpiffyRender>();
(Так как ваша стратегия рендеринга не имеет никакого состояния, это делает этот подход еще более благоприятным)
Если вы все время меняете свой рендерер, то этот шаблон / подход становится менее благоприятным … но если у вас есть камера, которая всегда делает один и тот же способ, это немного более хороший подход. Если у вашего рендерера есть состояние, вы все равно можете использовать этот метод; но вам понадобится ссылка на экземпляр внутри класса, и вы не будете использовать оператор Using ::. (В целом, при этом вы пишете меньше стандартного шаблона, вам не нужно делать какие-либо назначения или выделения во время выполнения, и компилятор работает для вас)
Подробнее об этом см .: http://en.wikipedia.org/wiki/Policy-based_design
Или читайте Modern C ++ Design … в любом случае это отличное чтение! http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315
Как не связанное в стороне: Вы можете рассмотреть некоторые из достоинств, которые дает вам C ++ x11. Это действительно очистит ваш код и сделает его более безопасным. (Особенно совместно используемые / уникальные / etc классы ptr.)