
Фабричный метод на C++
Фабричный метод — это порождающий паттерн проектирования, который решает проблему создания различных продуктов, без указания конкретных классов продуктов.
Фабричный метод задаёт метод, который следует использовать вместо вызова оператора new
для создания объектов-продуктов. Подклассы могут переопределить этот метод, чтобы изменять тип создаваемых продуктов.
Сложность:
Популярность:
Применимость: Паттерн можно часто встретить в любом C++ коде, где требуется гибкость при создании продуктов.
Признаки применения паттерна: Фабричный метод можно определить по создающим методам, которые возвращают объекты продуктов через абстрактные типы или интерфейсы. Это позволяет переопределять типы создаваемых продуктов в подклассах.
Концептуальный пример
Этот пример показывает структуру паттерна Фабричный метод, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
/** * Интерфейс Продукта объявляет операции, которые должны выполнять все * конкретные продукты. */ class Product { public: virtual ~Product() {} virtual std::string Operation() const = 0; }; /** * Конкретные Продукты предоставляют различные реализации интерфейса Продукта. */ class ConcreteProduct1 : public Product { public: std::string Operation() const override { return "{Result of the ConcreteProduct1}"; } }; class ConcreteProduct2 : public Product { public: std::string Operation() const override { return "{Result of the ConcreteProduct2}"; } }; /** * Класс Создатель объявляет фабричный метод, который должен возвращать объект * класса Продукт. Подклассы Создателя обычно предоставляют реализацию этого * метода. */ class Creator { /** * Обратите внимание, что Создатель может также обеспечить реализацию * фабричного метода по умолчанию. */ public: virtual ~Creator(){}; virtual Product* FactoryMethod() const = 0; /** * Также заметьте, что, несмотря на название, основная обязанность Создателя * не заключается в создании продуктов. Обычно он содержит некоторую базовую * бизнес-логику, которая основана на объектах Продуктов, возвращаемых * фабричным методом. Подклассы могут косвенно изменять эту бизнес-логику, * переопределяя фабричный метод и возвращая из него другой тип продукта. */ std::string SomeOperation() const { // Вызываем фабричный метод, чтобы получить объект-продукт. Product* product = this->FactoryMethod(); // Далее, работаем с этим продуктом. std::string result = "Creator: The same creator's code has just worked with " + product->Operation(); delete product; return result; } }; /** * Конкретные Создатели переопределяют фабричный метод для того, чтобы изменить * тип результирующего продукта. */ class ConcreteCreator1 : public Creator { /** * Обратите внимание, что сигнатура метода по-прежнему использует тип * абстрактного продукта, хотя фактически из метода возвращается конкретный * продукт. Таким образом, Создатель может оставаться независимым от * конкретных классов продуктов. */ public: Product* FactoryMethod() const override { return new ConcreteProduct1(); } }; class ConcreteCreator2 : public Creator { public: Product* FactoryMethod() const override { return new ConcreteProduct2(); } }; /** * Клиентский код работает с экземпляром конкретного создателя, хотя и через его * базовый интерфейс. Пока клиент продолжает работать с создателем через базовый * интерфейс, вы можете передать ему любой подкласс создателя. */ void ClientCode(const Creator& creator) { // ... std::cout << "Client: I'm not aware of the creator's class, but it still works.\n" << creator.SomeOperation() << std::endl; // ... } /** * Приложение выбирает тип создателя в зависимости от конфигурации или среды. */ int main() { std::cout << "App: Launched with the ConcreteCreator1.\n"; Creator* creator = new ConcreteCreator1(); ClientCode(*creator); std::cout << std::endl; std::cout << "App: Launched with the ConcreteCreator2.\n"; Creator* creator2 = new ConcreteCreator2(); ClientCode(*creator2); delete creator; delete creator2; return 0; }
Output.txt: Результат выполнения
App: Launched with the ConcreteCreator1. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct1} App: Launched with the ConcreteCreator2. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}