Factory и Abstract Factory
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 92 из 104.
Шаблон Factory делегирует создание объектов отдельному методу или class, позволяя вам create объекты без указания их точного class. Это полезно, когда логика создания сложна или когда вы хотите отделить клиентский код от concrete-реализаций.
Простая фабрика использует метод, который возвращает разные производные типы в зависимости от входных данных:
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// Фабричная функция
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}Abstract Factory развивает эту идею дальше, создавая семейства связанных объектов. Вместо одного фабричного метода у вас есть интерфейс factory с несколькими методами создания:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// Абстрактная фабрика
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};Используйте Factory, когда вам нужно гибкое создание объектов, а Abstract Factory — когда нужно убедиться, что связанные объекты согласованно работают вместе.
Задание
ЛегкоДавайте создадим систему Генератор документов с использованием шаблонов Factory и Abstract Factory. Вы создадите систему, способную генерировать различные типы документов (отчёты и счета) в разных выходных форматах (PDF и HTML), демонстрируя, как factory отделяют создание объектов от клиентского кода.
Вы организуете код в четырёх файлах:
Document.h: Определите иерархию продуктов документов.Создайте абстрактный базовый class
Documentс чистым виртуальным методомgenerate(), который возвращает строку с описанием сгенерированного документа, и виртуальным деструктором.Затем создайте четыре concrete class документов:
PDFReport:generate()возвращает"Generating PDF Report"HTMLReport:generate()возвращает"Generating HTML Report"PDFInvoice:generate()возвращает"Generating PDF Invoice"HTMLInvoice:generate()возвращает"Generating HTML Invoice"
DocumentFactory.h: Реализуйте простую factory function и abstract factory.Сначала создайте простую factory function с именем
createDocument, которая принимает две строки: тип документа ("report"или"invoice") и формат ("pdf"или"html"). Она должна возвращатьstd::unique_ptr<Document>, указывающий на подходящий concrete документ. Для недопустимых комбинаций возвращайтеnullptr.Затем создайте abstract factory class с именем
DocumentFactoryс двумя чистыми виртуальными методами:createReport(): возвращаетstd::unique_ptr<Document>createInvoice(): возвращаетstd::unique_ptr<Document>
Реализуйте две concrete factory:
PDFFactory: создаётPDFReportиPDFInvoiceHTMLFactory: создаётHTMLReportиHTMLInvoice
DocumentService.h: Создайте сервис, использующий abstract factory.Определите class
DocumentService, который принимаетstd::unique_ptr<DocumentFactory>в своём конструкторе. В нём должен быть метод с именемgenerateAllDocuments(), использующий factory для создания отчёта и счета, а затем выводящий результат вызоваgenerate()для каждого из них, по одному в строке.main.cpp: Продемонстрируйте оба подхода с factory.Считайте два входных значения:
- Тип документа (
reportилиinvoice) - Формат (
pdfилиhtml)
Сначала используйте простую factory function для создания документа на основе входных данных. Выведите результат вызова
generate()для него.Затем выведите пустую строку.
Далее создайте
DocumentServiceс подходящей factory на основе введённого формата (используйтеPDFFactoryдляpdf,HTMLFactoryдляhtml). ВызовитеgenerateAllDocuments(), чтобы показать, как abstract factory создаёт семейство связанных документов.- Тип документа (
Например, при входных данных report и pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceПри входных данных invoice и html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceОбратите внимание, как простая factory создаёт один документ на основе обоих параметров, тогда как abstract factory гарантирует, что все документы, созданные сервисом, используют один и тот же формат. Это ключевое преимущество Abstract Factory, когда вам нужны семейства связанных объектов, которые должны согласованно работать вместе.
Попробуйте сами
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// Прочитать входные данные
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: Используйте простую фабричную функцию для создания документа
// на основе входных данных documentType и format
// Выведите результат вызова generate() для него
// Выведите пустую строку
cout << endl;
// TODO: Создайте DocumentService с соответствующей фабрикой
// на основе входного format (PDFFactory для "pdf", HTMLFactory для "html")
// Вызовите generateAllDocuments() для демонстрации абстрактной фабрики
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++