Применение. Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектированияЭ. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес Приемы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб: «Питер», 2007. — С. 366.
Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288.
Порождающие паттерны проектирования Абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Шаблон, порождающий классы, использует наследование, чтобы изменять инстанцируемый класс, а шаблон, порождающий объекты, делегирует инстанцирование другому объекту. Инстанцирование — создание экземпляра класса. В отличие от слова «создание», применяется не к объекту, а к классу. То есть, говорят: «(в виртуальной среде) создать экземпляр класса или инстанцировать класс». Порождающие шаблоны используют полиморфное инстанцирование.
Использование Эти шаблоны оказываются важны, когда система больше зависит от композиции объектов, чем от наследования классов. Основной упор делается не на жестком кодировании фиксированного набора поведений, а на определении небольшого набора фундаментальных поведений, с помощью композиции которых можно получать любое число более сложных. Таким образом, для создания объектов с конкретным поведением требуется нечто большее, чем простое инстанцирование класса. Порождающие шаблоны инкапсулируют знания о конкретных классах, которые применяются в системе. Они скрывают детали того, как эти классы создаются и стыкуются. Единственная информация об объектах, известная системе, — это их интерфейсы, определенные с помощью абстрактных классов. Следовательно, порождающие шаблоны обеспечивают большую гибкость при решении вопроса о том, что создается, кто это создает, как и когда. Иногда допустимо выбирать между тем или иным порождающим шаблоном. Например, есть случаи, когда с пользой для дела можно использовать как прототип, так и абстрактную фабрику. В других ситуациях порождающие шаблоны дополняют друг друга. Так, применяя строитель, можно использовать другие шаблоны для решения вопроса о том, какие компоненты нужно строить, а прототип часто реализуется вместе с одиночкой. Порождающие шаблоны тесно связаны друг с другом, их рассмотрение лучше проводить совместно, чтобы лучше были видны их сходства и различия.
Перечень порождающих паттернов К порождающим паттернам проектирования относятся следующие: · абстрактная фабрика (abstract factory); · строитель (builder); · фабричный метод (factory method); · прототип (prototype); · одиночка (singleton)
Абстрактная фабрика Абстрактная фабрика — паттерн позволяющий изменять поведение системы, варьируя создаваемые объекты, при этом сохраняя интерфейсы. Он позволяет создавать целые группы взаимосвязанных объектов (продуктов), которые, будучи созданными одной фабрикой, реализуют общее поведение. Паттерн реализуется созданием абстрактного класса Factory, который представляет собой интерфейс для создания компонентов системы (например, для оконного интерфейса он может создавать окна, кнопки и т.д.). Затем пишутся наследующиеся от него классы, реализующие этот интерфейс. Иначе говоря, продукт это тот объект, который должен быть произведен, а фабрика предоставляет механизм для его создания.
Назначение Предоставляет интерфейс для создания семейств, взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов. Достоинства · изолирует конкретные классы;
Недостатки · сложно добавить поддержку нового вида продуктов. Применение · Система не должна зависеть от того, как создаются, компонуются и представляются входящие в нее объекты. · Входящие в семейство взаимосвязанные объекты должны использоваться вместе и вам необходимо обеспечить выполнение этого ограничения. · Система должна конфигурироваться одним из семейств составляющих ее объектов. · Требуется предоставить библиотеку объектов, раскрывая только их интерфейсы, но не реализацию.
При данной реализации клиент не должен знать ничего о конкретных классах. Он только запрашивает у фабрики (абстрактной) построение объекта (абстрактного) с соответствующим интерфейсом.
Пример #include <iostream>
// Абстрактный ProductA class Car { public: virtual void info() = 0; virtual ~Car(){} };
// Конкретный ProductA1 class Ford: public Car { public: virtual void info() { std::cout << "Ford" << std::endl; } };
// Конкретный ProductA2 class Toyota: public Car { public: virtual void info() { std::cout << "Toyota" << std::endl; } };
// Абстрактный ProductВ class Engine { public: virtual void getPower() = 0; virtual ~Engine(){} };
// Конкретный ProductB1 class FordEngine: public Engine { public: virtual void getPower() { std::cout << "Ford Engine 4.4" << std::endl; } };
// Конкретный ProductB2 class ToyotaEngine: public Engine { public: virtual void getPower() { std::cout << "Toyota Engine 3.2" << std::endl; } };
// AbstractFactory class CarFactory { public: Car* getNewCar() { return createCar(); }
Engine* getNewEngine() { return createEngine(); }
virtual ~ CarFactory(){} protected: virtual Car* createCar() = 0; virtual Engine* createEngine() = 0; };
// Конкретная Factory1 class FordFactory: public CarFactory { protected: // from CarFactory virtual Car* createCar() { return new Ford(); }
virtual Engine* createEngine() { return new FordEngine(); } };
// Конкретная Factory2 class ToyotaFactory: public CarFactory { protected: // from CarFactory virtual Car* createCar() { return new Toyota(); }
virtual Engine* createEngine() { return new ToyotaEngine(); } };
int main() { CarFactory* curFactory = NULL; Car* myCar = NULL; Engine* myEngine = NULL;
ToyotaFactory toyotaFactory; FordFactory fordFactory;
curFactory = &toyotaFactory;
myCar = curFactory->getNewCar(); myCar->info(); myEngine = curFactory->getNewEngine(); myEngine->getPower(); delete myCar; delete myEngine;
curFactory = &fordFactory; myCar = curFactory->getNewCar(); myCar->info(); myEngine = curFactory->getNewEngine(); myEngine->getPower(); delete myCar; delete myEngine; return 0; }
|