Паттерн Абстрактная фабрика на C++
Абстрактная фабрика – это порождающий паттерн проектирования, который решает проблему создания целых семейств связанных продуктов, без указания конкретных классов продуктов.
Абстрактная фабрика задаёт интерфейс создания всех доступных типов продуктов, а каждая конкретная реализация фабрики порождает продукты одной из вариаций. Клиентский код вызывает методы фабрики для получения продуктов, вместо самостоятельного создания с помощью оператора new
. При этом фабрика сама следит за тем, чтобы создать продукт нужной вариации.
Особенности паттерна на C++
Сложность:
Популярность:
Применимость: Паттерн можно часто встретить в C++ коде, особенно там, где требуется создание семейств продуктов (например, внутри фреймворков).
Признаки применения паттерна: Паттерн можно определить по методам, возвращающим фабрику, которая, в свою очередь, используется для создания конкретных продуктов, возвращая их через абстрактные типы или интерфейсы.
Концептуальный пример
Этот пример показывает структуру паттерна Абстрактная фабрика, а именно – из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
/**
* Каждый отдельный продукт семейства продуктов должен иметь базовый интерфейс.
* Все вариации продукта должны реализовывать этот интерфейс.
*/
class AbstractProductA {
public:
virtual ~AbstractProductA(){};
virtual std::string UsefulFunctionA() const = 0;
};
/**
* Конкретные продукты создаются соответствующими Конкретными Фабриками.
*/
class ConcreteProductA1 : public AbstractProductA {
public:
std::string UsefulFunctionA() const override {
return "The result of the product A1.";
}
};
class ConcreteProductA2 : public AbstractProductA {
std::string UsefulFunctionA() const override {
return "The result of the product A2.";
}
};
/**
* Базовый интерфейс другого продукта. Все продукты могут взаимодействовать друг
* с другом, но правильное взаимодействие возможно только между продуктами одной
* и той же конкретной вариации.
*/
class AbstractProductB {
/**
* Продукт B способен работать самостоятельно...
*/
public:
virtual ~AbstractProductB(){};
virtual std::string UsefulFunctionB() const = 0;
/**
* ...а также взаимодействовать с Продуктами A той же вариации.
*
* Абстрактная Фабрика гарантирует, что все продукты, которые она создает,
* имеют одинаковую вариацию и, следовательно, совместимы.
*/
virtual std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const = 0;
};
/**
* Конкретные Продукты создаются соответствующими Конкретными Фабриками.
*/
class ConcreteProductB1 : public AbstractProductB {
public:
std::string UsefulFunctionB() const override {
return "The result of the product B1.";
}
/**
* Продукт B1 может корректно работать только с Продуктом A1. Тем не менее, он
* принимает любой экземпляр Абстрактного Продукта А в качестве аргумента.
*/
std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override {
const std::string result = collaborator.UsefulFunctionA();
return "The result of the B1 collaborating with ( " + result + " )";
}
};
class ConcreteProductB2 : public AbstractProductB {
public:
std::string UsefulFunctionB() const override {
return "The result of the product B2.";
}
/**
* Продукт B2 может корректно работать только с Продуктом A2. Тем не менее, он
* принимает любой экземпляр Абстрактного Продукта А в качестве аргумента.
*/
std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override {
const std::string result = collaborator.UsefulFunctionA();
return "The result of the B2 collaborating with ( " + result + " )";
}
};
/**
* Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают
* различные абстрактные продукты. Эти продукты называются семейством и связаны
* темой или концепцией высокого уровня. Продукты одного семейства обычно могут
* взаимодействовать между собой. Семейство продуктов может иметь несколько
* вариаций, но продукты одной вариации несовместимы с продуктами другой.
*/
class AbstractFactory {
public:
virtual AbstractProductA *CreateProductA() const = 0;
virtual AbstractProductB *CreateProductB() const = 0;
};
/**
* Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика
* гарантирует совместимость полученных продуктов. Обратите внимание, что
* сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то
* время как внутри метода создается экземпляр конкретного продукта.
*/
class ConcreteFactory1 : public AbstractFactory {
public:
AbstractProductA *CreateProductA() const override {
return new ConcreteProductA1();
}
AbstractProductB *CreateProductB() const override {
return new ConcreteProductB1();
}
};
/**
* Каждая Конкретная Фабрика имеет соответствующую вариацию продукта.
*/
class ConcreteFactory2 : public AbstractFactory {
public:
AbstractProductA *CreateProductA() const override {
return new ConcreteProductA2();
}
AbstractProductB *CreateProductB() const override {
return new ConcreteProductB2();
}
};
/**
* Клиентский код работает с фабриками и продуктами только через абстрактные
* типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать
* любой подкласс фабрики или продукта клиентскому коду, не нарушая его.
*/
void ClientCode(const AbstractFactory &factory) {
const AbstractProductA *product_a = factory.CreateProductA();
const AbstractProductB *product_b = factory.CreateProductB();
std::cout << product_b->UsefulFunctionB() << "\n";
std::cout << product_b->AnotherUsefulFunctionB(*product_a) << "\n";
delete product_a;
delete product_b;
}
int main() {
std::cout << "Client: Testing client code with the first factory type:\n";
ConcreteFactory1 *f1 = new ConcreteFactory1();
ClientCode(*f1);
delete f1;
std::cout << std::endl;
std::cout << "Client: Testing the same client code with the second factory type:\n";
ConcreteFactory2 *f2 = new ConcreteFactory2();
ClientCode(*f2);
delete f2;
return 0;
}
Output.txt: Результат выполнения
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)
Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)