Розглянемо обидва варіанти реалізації паттерна Factory Method на прикладі процесу породження військових персонажів для нашої стратегічної гри. Її докладний опис можна знайти в розділі Що породжують патерни. Для спрощення демонстраційного коду будемо створювати військові персонажі для якоїсь абстрактної армії без урахування особливостей воюючих сторін.
// #include <iostream>
#include <vector>
enum Warrior_ID { Infantryman_ID=0, Archer_ID, Horseman_ID };
// Иерархия классов игровых персонажей
class Warrior
{
public:
virtual void info() = 0;
virtual ~Warrior() {}
// Параметризированный статический фабричный метод
static Warrior* createWarrior( Warrior_ID id );
};
class Infantryman: public Warrior
{
public:
void info() {
cout << "Infantryman" << endl;
}
};
class Archer: public Warrior
{
public:
void info() {
cout << "Archer" << endl;
}
};
class Horseman: public Warrior
{
public:
void info() {
cout << "Horseman" << endl;
}
};
// Реализация параметризированного фабричного метода
Warrior* Warrior::createWarrior( Warrior_ID id )
{
Warrior * p;
switch (id)
{
case Infantryman_ID:
p = new Infantryman();
break;
case Archer_ID:
p = new Archer();
break;
case Horseman_ID:
p = new Horseman();
break;
default:
assert( false);
}
return p;
};
// Создание объектов при помощи параметризированного фабричного метода
int main()
{
vector<Warrior*> v;
v.push_back( Warrior::createWarrior( Infantryman_ID));
v.push_back( Warrior::createWarrior( Archer_ID));
v.push_back( Warrior::createWarrior( Horseman_ID));
for(int i=0; i<v.size(); i++)
v[i]->info();
// ...
}
Представлений варіант паттерна Factory Method користується популярністю завдяки своїй простоті. У ньому статичний фабричний метод createWarrior () визначений безпосередньо в полиморфном базовому класі Warrior. Цей фабричний метод є параметризованих, тобто для створення об'єкта деякого типу в createWarrior () передається відповідний ідентифікатор типу.
З точки зору "чистоти" об'єктно-орієнтованого коду у цього варіанту є такі недоліки:
· Так як код по створенню об'єктів усіх можливих типів зосереджений в статичному фабричному методі класу Warrior, то базовий клас Warrior володіє знанням про всі похідних від нього класах, що є нетиповим для об'єктно-орієнтованого підходу.
· Подібне використання оператора switch (як в коді фабричного методу createWarrior ()) в об'єктно-орієнтованому програмуванні також не вітається.
Зазначені недоліки відсутні в класичній реалізації патерна Factory Method.
//
#include <iostream>
#include <vector>
// Иерархия классов игровых персонажей
class Warrior
{
public:
virtual void info() = 0;
virtual ~Warrior() {}
};
class Infantryman: public Warrior
{
public:
void info() {
cout << "Infantryman" << endl;
};
};
class Archer: public Warrior
{
public:
void info() {
cout << "Archer" << endl;
};
};
class Horseman: public Warrior
{
public:
void info() {
cout << "Horseman" << endl;
};
};
// Фабрики объектов
class Factory
{
public:
virtual Warrior* createWarrior() = 0;
virtual ~Factory() {}
};
class InfantryFactory: public Factory
{
public:
Warrior* createWarrior() {
return new Infantryman;
}
};
class ArchersFactory: public Factory
{
public:
Warrior* createWarrior() {
return new Archer;
}
};
class CavalryFactory: public Factory
{
public:
Warrior* createWarrior() {
return new Horseman;
}
};
// Создание объектов при помощи фабрик объектов
int main()
{
InfantryFactory* infantry_factory = new InfantryFactory;
ArchersFactory* archers_factory = new ArchersFactory ;
CavalryFactory* cavalry_factory = new CavalryFactory ;
vector<Warrior*> v;
v.push_back( infantry_factory->createWarrior());
v.push_back( archers_factory->createWarrior());
v.push_back( cavalry_factory->createWarrior());
for(int i=0; i<v.size(); i++)
v[i]->info();
// ...
}
Класичний варіант паттерна Factory Method використовує ідею полиморфной фабрики. Спеціально виділений для створення об'єктів поліморфний базовий клас Factory оголошує інтерфейс фабричного методу createWarrior (), а похідні класи його реалізують.
Представлений варіант паттерна Factory Method є найбільш поширеним, але не єдиним.
Можливі такі варіації:
· Клас Factory має реалізацію фабричного методу createWarrior () за замовчуванням.
· Фабричний метод createWarrior () класу Factory параметризованих типом створюваного об'єкта (як і у представленого раніше, простого варіанту Factory Method) і має реалізацію за замовчуванням. В цьому випадку, похідні від Factory класи необхідні лише для того, щоб визначити нестандартну поведінку createWarrior ().