Паттерны программирования игр
— Паттерны поведения
257
Сделаем объекты типа больше похожими на типы:
конструкторы
Наша текущая реализация позволяет нам конструиро-
вать монстра напрямую и передавать ему его род. По-
рядок действий в этом методе обратный по сравнению
с тем, как объекты инстанцируются в большинстве объ-
ектно-ориентированных языков: обычно мы не выделя-
ем пустую память, чтобы затем
присвоить
ей класс. На-
оборот, мы вызываем функцию конструктора в самом
классе, а она уже отвечает за получение нами нового
экземпляра.
Этот же самый паттерн мы можем применить для на-
ших объектов типа:
class Breed
{
public:
Monster* newMonster()
{
return new Monster(*this);
}
// 8 6 …
};
А затем классы используют их:
class Monster
{
friend class Breed;
public:
const char* getAttack()
{
return breed_.getAttack();
}
private:
Monster(Breed& breed)
: health_(breed.getHealth()),
«Паттерн» очень подхо-
дящее здесь слово. Речь
идет об одном из клас-
сических паттернов
проектирования: Фаб-
ричный Метод (Factory
Method).
В некоторых языках
данный паттерн приме-
няется для создания
всех
объектов. В Ruby,
Smalltalk, Objective-C
и других языках, где
классы являются объек-
тами, вы создаете новые
экземпляры, вызывая
метод самого объекта
класса.
258
Объект типа (Type Object) —
Паттерны программирования игр
breed_(breed)
{}
int health_; // 9 .
Breed& breed_;
};
Разница заключается в функции
newMonster()
класса
Breed
. Это наш фабричный метод «конструктор».
В нашей первой реализации создание монстра выгляде-
ло так:
Monster* monster = new Monster(someBreed);
После внесения изменений — так:
Monster* monster = someBreed.newMonster();
Итак, зачем это нужно? Сейчас создание объекта со-
стоит из двух шагов: выделение памяти и инициализа-
ция. Конструктор класса
Monster
позволяет нам делать
любые инициализации. В нашем примере мы только
храним род, но в настоящей игре будет еще загружаться
графика, инициализироваться ИИ, управляющий мон-
стром, и т. д.
Тем не менее все перечисленное будет происходить
после
выделения памяти. Еще до того, как мы вызовем
конструктор, у нас уже появится фрагмент памяти, со-
держащий нашего монстра. В играх часто нужен кон-
троль процесса создания объектов, для чего мы исполь-
зуем распределитель памяти или паттерн Пул объектов
(Object Pool) (с. 388).
Помещения «конструктора» именно в класс
Breed
дает нам возможность создать такую логику. Вместо вы-
зова простого
new
функция
newMonster()
может выде-
лить память из пула или на куче перед тем, как передать
управление классу
Monster
для инициализации. По-
мещая эту логику в класс
Breed
, в
единственную
функ-
цию, имеющую возможность создавать монстров, мы
Тут есть еще небольшое
отличие. Поскольку наш
пример кода написан
на С++, мы можем вос-
пользоваться одной его
приятной возможно-
стью —
дружественные
классы
.
Мы сделали конструк-
тор класса
Monster
приватным, запретив
вызывать его напрямую.
Дружественные классы
обходят наложенное
ограничение, так что
класс
Breed
все же смо-
жет это сделать. В итоге
единственный
способ
создать монстра —
пройти через
newMonster()
.
Do'stlaringiz bilan baham: |