Листинг 5
class Car
{
public:
virtual Car* clone const = 0;
virtual const char* getName const = 0;
virtual void setEngine(Engine* engine);
virtual Engine* getEngine const;
virtual void setWheels(Wheel* FR, Wheel* FL, Wheel* BR, Wheel* BL);
virtual void setFrontWheels(Wheel* FR, Wheel* FL);
virtual void setBackWheels(Wheel* BR, Wheel* BL);
...
};
Теперь в потомках класса Car нужно перезагрузить и реализовать этот метод, он должен создавать абсолютно идентичную новую копию объекта (см. листинг 6).
Листинг 6
class CarBMW5: public Car
{
public:
virtual Car* clone const
{
CarBMW5* car = new CarBMW5;
car->setEngine(getEngine->clone);
car->setFrontWheels(getWheelFR->clone, getWheelFL->clone);
car->setBackWheels(getWheelBR->clone, getWheelBL->clone);
...
return car;
}
...
};
Обратите внимание, что функция клонирования CarBMW5::clone вызывает функции клонирования для всех своих составных частей. Это стандартная практика: если использовать функцию клонирования в составном объекте, то нужно добавить ее во все объекты, которые он содержит. Таким образом, автомобиль легко клонирует себя, так как просто последовательно запускает процесс копирования всех составных частей и собирает из них новый автомобиль – свою полную копию.
Применений у этого паттерна может быть несколько, как уже было сказано выше. В случае с копированием объектов (Ctrl-C, Ctrl-V) мы будем иметь массив выделенных объектов и при нажатии на Ctrl-C просто создадим их копии, используя метод clone. А нажав на Ctrl-V, мы вставляем эти объекты в нужное место. Мы также можем заранее создать все возможные объекты (типы автомобилей) и поместить их в один массив. А потом легко создавать автомобили по имени: находим объект с нужным именем в массиве и вызываем его метод clone. Простой пример.
class CarFactory
{
std::vector
public:
CarCreator
{
mCarTemplates.push_back(new CarBMW5);
mCarTemplates.push_back(new CarToyotaHiace);
mCarTemplates.push_back(new CarLadaKalina);
}
Car* create(const char* name)
{
for(size_t i = 0; i < mCarTemplates.size; ++i)
if (strcmp(mCarTemplates[i]->getName, name) == 0)
return mCarTemplates[i]->clone;
return NULL;
}
};
«Одиночка»
Паттерн Singleton гарантирует, что объект какого-то класса будет создан только один раз. Это, пожалуй, самый спорный из всех шаблонов проектирования. Многие профессионалы объектно-ориентированного проектирования не рекомендуют применять этот паттерн, поскольку считают его обычным аналогом глобальных переменных. Самая простая реализация паттерна Singleton – это так называемый «синглтон Мейерса», где «Одиночка» представляет собой статический локальный объект (это решение небезопасно при работе с нитями).
template
{
public:
static T& instance
{
// у класса T есть конструктор по умолчанию
static T theSingleInstance;
return theSingleInstance;
}
};
Чтобы класс стал «Одиночкой», его достаточно породить от Singleton.
Обычно всякие фабрики и контейнеры должны присутствовать в системе в единственном экземпляре, поэтому их разумно порождать от Singleton. Расширим класс CarFactory из предыдущего примера.
class CarFactory: public Singleton
{
... дальше то же, что и в CarFactory из паттерна «Прототип»
};
Все обращения к такому объекту ведутся через метод Instance. Для создания автомобилей по названию нужно просто написать следующий код.
Car* bmw = CarFactory::instance. create(«BMW5»);
Car* hiace = CarFactory::instance. create(«ToyotaHiace»);
При этом объект типа CarFactory фактически будет создан в момент первого вызова метода instance, а удален только при выходе из программы.