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 para um método ou classe separada, permitindo que você crie objetos sem especificar sua classe exata. Isso é útil quando a lógica de criação é complexa ou quando você deseja desacoplar o código do cliente de implementações concretas.
Uma simple factory 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 factory
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;
}O Abstract Factory leva isso adiante ao criar famílias de objetos relacionados. Em vez de um único método de fábrica, você tem uma interface de fábrica com múltiplos 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"; }
};
// Abstract Factory
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 você precisar de criação flexível de objetos, e Abstract Factory quando você precisar garantir que objetos relacionados trabalhem juntos de forma consistente.
Desafio
FácilVamos construir um sistema de Gerador de Documentos usando os padrões Factory e Abstract Factory. Você criará um sistema que pode 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 do cliente.
Você organizará seu código em quatro arquivos:
Document.h: Defina sua hierarquia de produtos de documento.Crie uma classe base abstrata
Documentcom um método virtual purogenerate()que retorna uma string descrevendo o documento gerado, e um destrutor virtual.Em seguida, crie quatro classes de documentos concretas:
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 recebe 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 utiliza a fábrica abstrata.Defina uma classe
DocumentServiceque recebe umstd::unique_ptr<DocumentFactory>em seu construtor. Ela deve ter um método chamadogenerateAllDocuments()que usa a fábrica para criar tanto um relatório quanto uma fatura, e então imprime o resultado da chamada degenerate()em cada um, um por linha.main.cpp: Demonstre ambas as 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 baseada 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 — este é o principal benefício do Abstract Factory quando você precisa de famílias de objetos relacionados que trabalham juntos de forma consistente.
Folha de consulta
O padrão Factory delega a criação de objetos para um método ou classe separada, permitindo que você crie objetos sem especificar sua classe exata. Isso desacopla o código do cliente das implementações concretas.
Uma factory simples usa uma função que retorna diferentes tipos derivados com base na entrada:
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"; }
};
// Factory function
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;
}O padrão Abstract Factory cria famílias de objetos relacionados. Em vez de um único método factory, você tem uma interface factory com múltiplos 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"; }
};
// Abstract Factory
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 criação flexível de objetos. Use Abstract Factory quando precisar garantir que objetos relacionados trabalhem juntos de forma consistente como uma família.
Experimente você mesmo
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// Lê as 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 da chamada de generate() nele
// Imprime 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 OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários10Visão Geral da STL
Visão Geral e Filosofia da STLContainers 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)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico