Я сделал теневой поступок

Приемлемы ли (кажущиеся) теневые вещи из практических соображений?

Во-первых, немного предыстории моего кода. Я пишу графический модуль своей 2D-игры. Мой модуль содержит более двух классов, но я упомяну здесь только два: Font и GraphicsRenderer .

Font предоставляет интерфейс для загрузки (и release) и не более того. В моем заголовке Font я не хочу, чтобы какие-либо детали реализации просачивались, включая типы данных сторонней библиотеки, которую я использую. Я предотвращаю отображение сторонней библиотеки в заголовке с помощью неполного типа (я понимаю, что это стандартная практика):

class Font
{
  private:
    struct FontData;
    boost::shared_ptr<FontData> data_;
};

GraphicsRenderer - это устройство (читай: одноэлементное) который инициализирует и завершает стороннюю графическую библиотеку, а также используется для визуализации графических объектов (например, шрифтов , изображений и т. д.). Причина этого ' sa singleton - это потому, что, как я сказал, класс автоматически инициализирует стороннюю библиотеку; он делает это, когда синглтон создается, и выходит из библиотеки, когда синглтон уничтожается.

В любом случае, чтобы GR мог отображать Шрифт , он, очевидно, должен иметь доступ к его объекту FontData . Один из вариантов - иметь общедоступный получатель, но при этом будет открыта реализация Font (никакой другой класс, кроме Font и GR , не должен заботиться о ] FontData ). Вместо этого я решил, что лучше сделать GR другом Font .

Примечание: до сих пор я делал две вещи, которые некоторые могут посчитать сомнительными (одиночка и друг), но это не то, о чем я хочу вас спросить. Тем не менее, если вы считаете, что мои доводы в пользу того, чтобы сделать GR синглтоном и другом Font ] неверны, критикуйте меня и, возможно, предложите лучшие решения.

Темная вещь. ] Итак, GR имеет доступ к Font :: data _ хотя и дружелюбно, но как он точно знает, что такое FontData (поскольку он не определен в заголовке, это неполный тип)? Я просто покажу код и комментарий, который включает обоснование ...

// =============================================================================
//   graphics/font.cpp
// -----------------------------------------------------------------------------

struct Font::FontData
    : public sf::Font
{
    // Just a synonym of sf::Font
};

// A redefinition of FontData exists in GraphicsRenderer::printText(),
// which will have to be modified as well if this definition is modified.
// (The redefinition is called FontDataSurogate.)
// Why not have FontData defined only once in a separate header:
// If the definition of FontData changes, most likely printText() text will
// have to be altered also regardless. Considering that and also that FontData
// has (and should have) a very simple definition, a separate header was
// considered too much of an overhead and of little practical advantage.


// =============================================================================
//   graphics/graphics_renderer.cpp
// -----------------------------------------------------------------------------

void GraphicsRenderer::printText(const Font& fnt /* ... */)
{
    struct FontDataSurogate
        : public sf::Font {
    };

    FontDataSurogate* suro = (FontDataSurogate*)fnt.data_.get();
    sf::Font& font = (sf::Font)(*suro);

    // ...
}

Так что это мрачная вещь, которую я пытаюсь сделать. В основном то, что я хочу, - это обзор моего обоснования, поэтому скажите, пожалуйста, если вы думаете, что я совершил что-то ужасное , или, если нет, подтвердите мое обоснование, чтобы я мог быть немного увереннее » я поступаю правильно. :) (Это мой самый большой проект, и я только начинаю, так что я как бы чувствую вещи в темноте.)

5
задан Paul Manta 8 April 2011 в 23:40
поделиться