Управление объектами - контейнер или фабрика? - PullRequest
1 голос
/ 12 октября 2011

Я близок к завершению вводного урока по программированию игр и хотел бы объединить навыки, которые я выучил в классе, с моим предыдущим опытом ООП, чтобы создать небольшую библиотеку для создания 2D-игр.Моя текущая проблема, тем не менее, это лучший способ управлять коллекциями экземпляров моих классов.

Используемая мной библиотека (DarkGDK) полностью состоит из свободных функций, которые действуют на целые числа.Когда «объект» создается с помощью функции, такой как dbSprite (), вы присваиваете ему уникальный идентификатор (int значение) для ссылки на него - я думаю, это своего рода «адрес».Лично я считаю этот подход ужасным, поэтому я создал классы для инкапсуляции каждого набора бесплатных функций, таких как Image, Sprite и AnimatedSprite (два типа спрайтов различаются в библиотеке DarkGDK.)

Проблема в том, чтоЧтобы эти объекты работали, мне все равно нужно передать уникальный идентификатор конструктору, чтобы вызывать функции DarkGDK для соответствующего адреса.Я пытаюсь отойти от ссылки на эти вещи по идентификатору все вместе, но я спорю о том, как объекты должны быть созданы.На данный момент у меня есть несколько классов AssetManager, которые хранят ссылки на каждый созданный объект, проверяют существующие идентификаторы и разрешают только уникальные, но это по-прежнему не решает проблему принудительной генерации идентификатора, внешнего для класса управления.Это привело меня к мысли, что Factory - лучший подход.

Я знаю, что в C # я мог бы создать AssetFactory<T> where T:Asset, который может легко вызывать соответствующие конструкторы для каждого актива для создания экземпляров, но, насколько мне известно, C ++не имеет таких возможностей.

Так что я думаю, что подход, который я должен использовать, - это использование какого-то абстрактного AssetFactory. Моя идея (правильная или нет) состоит в том, что дочерние элементы AssetFactory будут отслеживать используемые идентификаторы и выдают только уникальные идентификаторы объектов.Примерно так:

class Asset {
    int m_index;
    Asset(int index);
};
class Image : public Asset {
    Image(char* imgPath);
    void Draw();
};
class Sprite : public Asset {
    Sprite(Image* img);
    void Draw();
};

class AssetFactory {
private:
    std::vector<Asset*> m_assets;
    int GetUniqueID();
public:
    AssetFactory();
    ~AssetFactory();

    virtual Asset* CreateAsset(); // but each class has different constructor parameters...
};

class ImageFactory : public AssetFactory {
    Asset* CreateAsset(char* imgPath); // ...so this wouldn't work (nor compile)
};
class SpriteFactory : public AssetFactory {
    Asset* CreateAsset();   // ...so will i be forced to call the default constructor and modify it later?
};

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

РЕДАКТИРОВАТЬ: Для пояснения я хочу выделить отдельные фабрики для спрайтов и изображений, потому что равен допустимо для спрайта и изображения иметь одинаковый идентификатор.Идентификаторы должны быть уникальными только среди других активов того же «типа».

Ответы [ 3 ]

1 голос
/ 13 октября 2011

Это довольно тривиальная проблема, которую нужно решить, если ваша библиотека допускает произвольные идентификаторы и вы работаете в относительно равном адресном пространстве (например, sizeof (int) == sizeof (int *)), что верно практически для всех32-битные компиляторы, о которых я знаю.Генерация идентификаторов тогда тривиальна - просто переинтерпретируйте указатель.

class Sprite {
    int GetUniqueID() { return reinterpret_cast<int>(this); } // easies
public:
    // public interface
};

Кроме того, на самом деле, вероятно, не стоит повторно использовать старые идентификаторы.Просто продолжай делать новые.Я имею в виду, вам не хватит места в 32-битном целом числе.

Наконец, вы определенно не можете использовать здесь наследование во время выполнения.Если нужно, используйте миксин времени компиляции.

0 голосов
/ 13 октября 2011

Может быть, я что-то упустил, мне было непонятно, почему вы не можете переместить генератор идентификаторов в AssetManager, таким образом скрывая все страдания уникальных идентификаторов из внешнего мира.

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

0 голосов
/ 12 октября 2011

Там, где у C # есть дженерики, у C ++ есть шаблоны.Вы не можете так легко предоставить ограничения для универсального параметра в C ++, но есть способы убедиться, что вы даете только тип производного от Asset в качестве параметра шаблона.

Для передачиПрисвоив параметры конструктору, вы можете использовать метод шаблона variadic.В настоящий момент у меня нет доступного мне компилятора ... Я отредактирую позже с примером, хотя вы можете найти много другого кода шаблона variadic в stackoverflow.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...