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

0

Я планирую дизайн для моего класса Window. Цель состоит в том, чтобы обеспечить абстракцию для создания агностического окна платформы, готового для рендеринга OpenGL. Я думаю о том, что класс "Окно" будет публичным интерфейсом, а класс WindowImpl обрабатывает работу. Будет ли Window другом WindowImpl и вызовом функций WindowImpl внутри Window, вызывают проблемы? Технически, WindowImpl не будет создан правильно? Таким образом, деструктор не будет называться, что означает, что деструктор окна не будет вызван, поэтому потребуется функция destroy. 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
    Похоже, это должно быть на CodeReview.SE? (В любом случае вы должны проверить GitHub для SFML)
Теги:
class-design

1 ответ

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.

Единственный улов - это то, как вы строите Window и его Impl. Традиционным способом является использование абстрактной фабрики:

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
    Я не совсем понимаю цель абстрактной фабрики. Почему бы не иметь конструктор WindowImpl (std :: unique_ptr <Window>), который устанавливает mOwner для переданного окна, а конструктор Window установить mImpl = std :: unique_ptr <WindowImpl> (new WindowImpl (this))?
  • 0
    Вы не можете создать WindowImpl потому что он абстрактный. Цель абстрактной фабрики - создать WindowImplX11 (и, конечно, подключить его к собственному Window ).
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню