Factory e Abstract Factory
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 92 de 104.
O padrão Factory delega a criação de objetos a um método ou classe separado, permitindo criar objetos sem especificar sua classe exata. Isso é útil quando a lógica de criação é complexa ou quando você deseja desacoplar o código cliente das implementações concretas.
Uma fábrica simples usa um método que retorna diferentes tipos derivados com base na entrada:
#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"; }
};
// Função de fábrica
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;
}A Abstract Factory leva isso além, criando famílias de objetos relacionados. Em vez de um método de fábrica, você tem uma interface de fábrica com vários métodos de criação:
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"; }
};
// Fábrica Abstrata
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>();
}
};Use Factory quando precisar de uma criação flexível de objetos e Abstract Factory quando precisar garantir que objetos relacionados funcionem juntos de forma consistente.
Desafio
FácilVamos criar um sistema de Gerador de Documentos usando os padrões Factory e Abstract Factory. Você criará um sistema capaz de gerar diferentes tipos de documentos (relatórios e faturas) para diferentes formatos de saída (PDF e HTML), demonstrando como as fábricas desacoplam a criação de objetos do código cliente.
Você organizará seu código em quatro arquivos:
Document.h: Defina a hierarquia de produtos de documentos.Crie uma classe base abstrata
Documentcom um método virtual purogenerate()que retorna uma string descrevendo o documento gerado, além de um destrutor virtual.Em seguida, crie quatro classes concretas de documentos:
PDFReport:generate()retorna"Generating PDF Report"HTMLReport:generate()retorna"Generating HTML Report"PDFInvoice:generate()retorna"Generating PDF Invoice"HTMLInvoice:generate()retorna"Generating HTML Invoice"
DocumentFactory.h: Implemente tanto uma função de fábrica simples quanto uma fábrica abstrata.Primeiro, crie uma função de fábrica simples chamada
createDocumentque receba duas strings: um tipo de documento ("report"ou"invoice") e um formato ("pdf"ou"html"). Ela deve retornar umstd::unique_ptr<Document>apontando para o documento concreto apropriado. Retornenullptrpara combinações inválidas.Em seguida, crie uma classe de fábrica abstrata chamada
DocumentFactorycom dois métodos virtuais puros:createReport(): retorna umstd::unique_ptr<Document>createInvoice(): retorna umstd::unique_ptr<Document>
Implemente duas fábricas concretas:
PDFFactory: criaPDFReportePDFInvoiceHTMLFactory: criaHTMLReporteHTMLInvoice
DocumentService.h: Crie um serviço que use a fábrica abstrata.Defina uma classe
DocumentServiceque receba umstd::unique_ptr<DocumentFactory>em seu construtor. Ela deve ter um método chamadogenerateAllDocuments()que use a fábrica para criar um relatório e uma fatura, e então imprima o resultado da chamada degenerate()em cada um, um por linha.main.cpp: Demonstre as duas abordagens de fábrica.Leia duas entradas:
- Um tipo de documento (
reportouinvoice) - Um formato (
pdfouhtml)
Primeiro, use a função de fábrica simples para criar um documento com base nas entradas. Imprima o resultado da chamada de
generate()nele.Em seguida, imprima uma linha em branco.
Depois, crie um
DocumentServicecom a fábrica apropriada com base na entrada de formato (usePDFFactoryparapdf,HTMLFactoryparahtml). ChamegenerateAllDocuments()para mostrar como a fábrica abstrata cria uma família de documentos relacionados.- Um tipo de documento (
Por exemplo, com as entradas report e pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceCom as entradas invoice e html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceObserve como a fábrica simples cria um único documento com base em ambos os parâmetros, enquanto a fábrica abstrata garante que todos os documentos criados por um serviço compartilhem o mesmo formato. Esse é o principal benefício do Abstract Factory quando você precisa de famílias de objetos relacionados que funcionem juntas de maneira consistente.
Experimente você mesmo
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// Ler entradas
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: Use a função de fábrica simples para criar um documento
// com base nas entradas documentType e format
// Imprima o resultado de chamar generate() nele
// Imprima uma linha em branco
cout << endl;
// TODO: Crie um DocumentService com a fábrica apropriada
// com base na entrada format (PDFFactory para "pdf", HTMLFactory para "html")
// Chame generateAllDocuments() para demonstrar a fábrica abstrata
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online