Проектирование класса Window

Я планирую дизайн для моего класса Window. Цель состоит в том, чтобы предоставить абстракцию для создания независимого от платформы окна, готового для рендеринга OpenGL. Я думаю о том, чтобы класс «Window» был общедоступным интерфейсом, а класс «WindowImpl» обрабатывал эту работу. Будет ли создание Window другом WindowImpl и вызов функций WindowImpl внутри Window вызывает проблемы? Технически, WindowImpl не будет создан правильно? Таким образом, деструктор не будет вызван, что означает, что деструктор Window не будет вызван, поэтому потребуется функция уничтожения. Ex.

class MyWindow
{
public:
void create(width, height, title)
{
WindowImpl::create(width, height, title);
open = true;
}

void close()
{
WindowImpl::destroy();
open = false;
}

bool isOpen()
{
return open;
}

private:
bool open;
};

class WindowImpl
{
friend class MyWindow;

private:
static void create(width, height, title) {} // Creates the window
static void destroy()
{
XCloseDisplay(display);
}

static Display* display;
static Window window;
static GLXContext context;
};

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

0

Решение

Если он действительно должен быть независимым от платформы, то я предлагаю использовать что-то вроде этого:

class WindowImpl
{
public:
virtual void setOwner(Window* pOwner) = 0
virtual void create(width, height, title) = 0;
virtual void close() = 0;
};

class Window
{
public:

void create(width, height, title)
{
mImpl->create(width, height, title);
open = true;
}

void close()
{
open = false;
mImpl->destroy();
}

Window(std::unique_ptr<WindowImpl> pImpl)
: mImpl(pImpl)
{
}

private:
std::unique_ptr<WindowImpl> mImpl;
};

// Then off somewhere else...
class WindowImplX11 : public WindowImpl
{
public:
void setOwner(Window* pOwner) {
mOwner = pOwner;
}

void destroy() {
XCloseDisplay(display);
}private:
// Pointer back to the owner just in case (e.g. event
// delivery); if weak_ptr worked with unique_ptr, that
// would be better.
Window* mOwner;

Display* display;
GLXContext context;
};

Это облегченная версия шаблона Bridge, которая обычно используется, когда у вас есть две несовместимые иерархии объектов, которые необходимо связать вместе. Это вырожденный случай (поскольку в «иерархии» есть только один класс), но это все еще полезная техника для размышлений. Вероятно, наиболее известным примером этого является Java AWT (однако AWT называет его «Peer», а не «Impl»).

То, как именно вы распределяете обязанности между интерфейсом и бэкендом, — это, конечно, то, что вам нужно решить для себя, и, вероятно, будут некоторые изменения. Например, вы можете решить, что контекст OpenGL является достаточно важной концепцией, и вам необходимо предоставить ее клиентам. То же самое касается таких вещей, как vsync fences, которые еще не полностью поддерживаются стандартным способом. Да, я смотрю на тебя, OS X.

Единственный улов — это то, как вы строите окно и его значение. Традиционный способ — использовать абстрактную фабрику:

class Toolkit
{
public:
std::unique_ptr<Window> createWindow() = 0;
};

// then elsewhere...

// Should have been in the library, only left out by oversight.
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}

class ToolkitX11 : public Toolkit
{
public:
std::unique_ptr<Window>
createWindow() {
return make_unique<Window>(make_unique<WindowImplX11>());
}
};

Есть и другие способы, которые немного более современны.

0

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector