Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 Document com um método virtual puro generate() que retorna uma string descrevendo o documento gerado, e um destrutor virtual.

    Em seguida, crie quatro classes de documentos concretas:

    • PDFReportgenerate() retorna "Generating PDF Report"
    • HTMLReportgenerate() retorna "Generating HTML Report"
    • PDFInvoicegenerate() retorna "Generating PDF Invoice"
    • HTMLInvoicegenerate() 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 createDocument que recebe duas strings: um tipo de documento ("report" ou "invoice") e um formato ("pdf" ou "html"). Ela deve retornar um std::unique_ptr<Document> apontando para o documento concreto apropriado. Retorne nullptr para combinações inválidas.

    Em seguida, crie uma classe de fábrica abstrata chamada DocumentFactory com dois métodos virtuais puros:

    • createReport() — retorna um std::unique_ptr<Document>
    • createInvoice() — retorna um std::unique_ptr<Document>

    Implemente duas fábricas concretas:

    • PDFFactory — cria PDFReport e PDFInvoice
    • HTMLFactory — cria HTMLReport e HTMLInvoice
  • DocumentService.h: Crie um serviço que utiliza a fábrica abstrata.

    Defina uma classe DocumentService que recebe um std::unique_ptr<DocumentFactory> em seu construtor. Ela deve ter um método chamado generateAllDocuments() que usa a fábrica para criar tanto um relatório quanto uma fatura, e então imprime o resultado da chamada de generate() em cada um, um por linha.

  • main.cpp: Demonstre ambas as abordagens de fábrica.

    Leia duas entradas:

    1. Um tipo de documento (report ou invoice)
    2. Um formato (pdf ou html)

    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 DocumentService com a fábrica apropriada baseada na entrada de formato (use PDFFactory para pdf, HTMLFactory para html). Chame generateAllDocuments() para mostrar como a fábrica abstrata cria uma família de documentos relacionados.

Por exemplo, com as entradas report e pdf:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

Com as entradas invoice e html:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

Observe 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;
}
quiz iconTeste seus conhecimentos

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