Создание параметризованного класса с утверждением наличия параметра.Использовать фабрику? - PullRequest
1 голос
/ 11 ноября 2010

Я провожу около 2 часов, читая множество тем, связанных с фабрикой, и до сих пор не уверен, что это был бы правильный подход.

Вот в чем дело: у меня есть Враг класс, который содержит переменные-члены, такие как name _ и health _.Я хочу создать его экземпляры с разными значениями этих переменных-членов, и мой первый подход состоял в том, чтобы получить параметры из статического массива со свойствами:

Enemy::Enemy(int type) : name_(properties[type].name),
                             health_(properties[type].health)
{
...
}

Проблема в том, что я не могу проверитьесли массив properties уже заполнен.Мне нужно было бы проверить это извне, вызвав статическую функцию Enemy :: initArray () , но это нарушило бы инкапсуляцию класса Enemy .

Этовремя для создания фабрики, которая инициализирует массив properties в своем конструкторе?А затем создайте врагов с помощью:

Enemy* EnemyFactory::create(type);

Я прочитал, что обычно фабрики создаются, когда у вас есть сложные иерархии классов, или вызывающему фабрике нужно знать только интерфейс созданных классов.Мне нужна только инкапсуляция создания и проверки массива.Есть ли "более легкое" решение?

РЕДАКТИРОВАТЬ: Я постараюсь быть более ясным:

1.) Я думаю, я знаю как создать фабрику.Основной вопрос - есть ли альтернатива !

2.) Я не хочу устанавливать свойства внутри объекта, но использую списки инициализации.Если я сделаю первое, я могу просто проверить массив в конструкторе и вообще не нуждаться в фабрике.

Ответы [ 4 ]

1 голос
/ 12 ноября 2010

Существуют три основных варианта:

  1. Каждый может указать начальное имя и состояние для Enemy, которое принимает оба параметра в качестве (открытого) конструктора.

  2. Конструктор класса Enemy отображает 'Enemy ID' в соответствующие значения свойств.Проблемы в этом подходе возникают, когда вам необходимо проверить достоверность «Enemy ID» и / или наличие сопоставления, а также использовать списки инициализаторов для свойств.Обычно это решается добавлением фиктивного члена / базового класса, который инициализируется вызовом функции проверки.
    Обычно это выглядит так:

    class Enemy
    {
        bool dummy_must_be_first;
    public:
        Enemy(int type) : dummy_must_be_first(validate(type), name(properties[type].name), health(properties[type].health) {}

    private:
        bool validate(int type)
        {
            // ensure properties is initialised and verify type is within range...
            // on failure, throw an exception

            return true;
        }

        string name;
        int health;
    };
  1. Вы используете фабричную функцию для выполнения сопоставления между «ID врага» и свойствами, которые используются для инициализации объекта Enemy.

Нет необходимости иметь отдельный фабричный класс.Для этого достаточно фабричного метода:

class Enemy
{
private:
    Enemy(string name_, int health_) : name(name_), health(health_) {}

    string name;
    int health;
public:
    static auto_ptr<Enemy> createEnemy(int type)
    {
        // ensure properties is initialised and verify type is within range...

        return auto_ptr<Enemy>(new Enemy(properties[type].name, properties[type].health));
    }
};
0 голосов
/ 11 ноября 2010

Ваш завод будет иметь ссылку на свойства. Враг должен принять имя и здоровье в качестве своих параметров в конструкторе.

Тогда что-то вроде:

Enemy* EnemyFactory::create(int type) const
{
   return new Enemy( properties[type].name, properties[type].health );
}

Если эти свойства не найдены, вам нужно обработать этот случай, либо выдав соответствующее сообщение об ошибке, либо допустив значение по умолчанию.

0 голосов
/ 11 ноября 2010

Предложение 1

Как насчет введения EnemyCreator?

class EnemyCreator
{
protected:
    Enemy* createImpl() const = 0;

public:
    // ctors

    Enemy* create() const
    {
        return(createImpl());
    }; // eo create
};  // eo class EnemyCreator


// a concrete creator

class EnemyType1Creator : public EnemyCreator
{
protected:

    Enemy* createImpl() const
    {
        Properties p;
        // ...
        // fill in properties here
        //
        return new Enemy(p); // Enemy will populate
    };
}; // eo class EnemyType1Creator

Затем измените ваш EnemyFactor для управления кучей объектов EnemyCreator.

Предложение 2

Вы говорите, что количество и типы набора свойств одинаковы, они просто различаются у разных врагов.Другим способом было бы иметь базовый класс, который устанавливает свойства противника в общем виде (предполагается, что _property является членом Properties и доступен):

class EnemyProperties : public Properties
{
protected:
    virtual void initialiseImpl() = 0;

public:
    void initialise()
    {
        // set up the properties all enemies have
        _property["healh"] = 5;
        _property["skill"] = 0.5f;
        _property["skillWithABanana"] = 1.0f;

        // now let derived classes specialise
        initialiseImpl();
    };
}; // eo class EnemyProperties

Теперь мы можем сделать наборы свойствдля любого количества типов и измените только то, что мы хотим:

class BadBananaEnemy : public EnemyProperties
{
protected:
    void initialiseImpl()
    {
        _property["skillWithABanana"] = 0.0f; // he's bad at this.
    }; // eo initialiseImpl()
}; // eo class BadBananaEnemy

и так далее.

Затем вы можете поставить их на фабрику, которая может производить их по имени:

class EnemyFactory
{
private:
    typedef std::pair<std::string, EnemyProperties> EnemyPair;
    typedef std::map<std::string, EnemyProperties> EnemyMap;
    EnemyMap m_EnemyTypes;

public:
    // register a type
    void registerType(const std::string& _name, const EnemyProperties& _prop)
    {
        m_EnemyTypes.insert(EnemyPair(_name, _prop));
    }; // eo registerType

    // create an enemy
    Enemy* createEnemy(const std::string& _type)
    {
        EnemyMap::iterator it(m_EnemyTypes.find(_type));
        if(it != m_EnemyTypes.end())
            return(NULL);
        else
            return(new Enemey(it->second));
    }; // eo createEnemy
}; // eo class EnemyFactory
0 голосов
/ 11 ноября 2010

Я вижу две возможности.

name_(default_name_value), health_(default_health_value) и затем выполняю фактическое присвоение в теле конструктора

или

, используя метод построения

...