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<Car*> mCarTemplates;
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<typename T> class Singleton
{
public:
static T& instance()
{
// у класса T есть конструктор по умолчанию
static T theSingleInstance;
return theSingleInstance;
}
};
Чтобы класс стал «Одиночкой», его достаточно породить от Singleton.
Обычно всякие фабрики и контейнеры должны присутствовать в системе в единственном экземпляре, поэтому их разумно порождать от Singleton. Расширим класс CarFactory из предыдущего примера.
class CarFactory: public Singleton<CarFactory>
{
... дальше то же, что и в CarFactory из паттерна «Прототип»
};
Все обращения к такому объекту ведутся через метод Instance(). Для создания автомобилей по названию нужно просто написать следующий код.
Car* bmw = CarFactory::instance(). create(«BMW5»);
Car* hiace = CarFactory::instance(). create(«ToyotaHiace»);
При этом объект типа CarFactory фактически будет создан в момент первого вызова метода instance(), а удален только при выходе из программы.
Итак, мы рассмотрели в этой статье первую группу классических паттернов – «Порождающие». Их нужно знать и понимать очень хорошо, они встречаются наиболее часто, ведь создание объектов – неотъемлемая часть любого алгоритма.
Продолжение следует.
Обезопасим себя откражи личной информации