Menu
Coddy logo textTech

Factory y Abstract Factory

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 92 de 104.

El patrón Factory delega la creación de objetos a un método o clase independiente, lo que permite crear objetos sin especificar su clase exacta. Esto es útil cuando la lógica de creación es compleja o cuando se desea desacoplar el código del cliente de las implementaciones concretas.

Una fábrica simple utiliza un método que devuelve diferentes tipos derivados basándose en la 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"; }
};

// Función 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;
}

El Abstract Factory lleva esto más allá al crear familias de objetos relacionados. En lugar de un solo método de fábrica, tienes una interfaz de fábrica con múltiples métodos de creación:

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 Abstracta
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>();
    }
};

Usa Factory cuando necesites una creación de objetos flexible, y Abstract Factory cuando necesites asegurar que los objetos relacionados funcionen juntos de manera consistente.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de Generador de Documentos utilizando los patrones Factory y Abstract Factory. Crearás un sistema que puede generar diferentes tipos de documentos (informes y facturas) para diferentes formatos de salida (PDF y HTML), demostrando cómo las fábricas desacoplan la creación de objetos del código del cliente.

Organizarás tu código en cuatro archivos:

  • Document.h: Define tu jerarquía de productos de documentos.

    Crea una clase base abstracta Document con un método virtual puro generate() que devuelva un string describiendo el documento generado, y un destructor virtual.

    Luego crea cuatro clases de documentos concretas:

    • PDFReportgenerate() devuelve "Generating PDF Report"
    • HTMLReportgenerate() devuelve "Generating HTML Report"
    • PDFInvoicegenerate() devuelve "Generating PDF Invoice"
    • HTMLInvoicegenerate() devuelve "Generating HTML Invoice"
  • DocumentFactory.h: Implementa tanto una función de fábrica simple como una fábrica abstracta.

    Primero, crea una función de fábrica simple llamada createDocument que reciba dos strings: un tipo de documento ("report" o "invoice") y un formato ("pdf" o "html"). Debe devolver un std::unique_ptr<Document> que apunte al documento concreto apropiado. Devuelve nullptr para combinaciones inválidas.

    Luego crea una clase de fábrica abstracta llamada DocumentFactory con dos métodos virtuales puros:

    • createReport() — devuelve un std::unique_ptr<Document>
    • createInvoice() — devuelve un std::unique_ptr<Document>

    Implementa dos fábricas concretas:

    • PDFFactory — crea PDFReport y PDFInvoice
    • HTMLFactory — crea HTMLReport y HTMLInvoice
  • DocumentService.h: Crea un servicio que utilice la fábrica abstracta.

    Define una clase DocumentService que reciba un std::unique_ptr<DocumentFactory> en su constructor. Debe tener un método llamado generateAllDocuments() que utilice la fábrica para crear tanto un informe como una factura, y luego imprima el resultado de llamar a generate() en cada uno, uno por línea.

  • main.cpp: Demuestra ambos enfoques de fábrica.

    Lee dos entradas:

    1. Un tipo de documento (report o invoice)
    2. Un formato (pdf o html)

    Primero, utiliza la función de fábrica simple para crear un documento basado en las entradas. Imprime el resultado de llamar a generate() en él.

    Luego imprime una línea en blanco.

    A continuación, crea un DocumentService con la fábrica apropiada basada en la entrada del formato (usa PDFFactory para pdf, HTMLFactory para html). Llama a generateAllDocuments() para mostrar cómo la fábrica abstracta crea una familia de documentos relacionados.

Por ejemplo, con las entradas report y pdf:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

Con las entradas invoice y html:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

Observa cómo la fábrica simple crea un solo documento basado en ambos parámetros, mientras que la fábrica abstracta asegura que todos los documentos creados por un servicio compartan el mismo formato; este es el beneficio clave de Abstract Factory cuando necesitas familias de objetos relacionados que trabajen juntos de manera consistente.

Hoja de referencia

El patrón Factory delega la creación de objetos a un método o clase independiente, lo que permite crear objetos sin especificar su clase exacta. Esto desacopla el código del cliente de las implementaciones concretas.

Una fábrica simple utiliza una función que devuelve diferentes tipos derivados según la 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;
}

El patrón Abstract Factory crea familias de objetos relacionados. En lugar de un único método de fábrica, se tiene una interfaz de fábrica con múltiples métodos de creación:

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>();
    }
};

Utilice Factory cuando necesite una creación de objetos flexible. Utilice Abstract Factory cuando necesite asegurarse de que los objetos relacionados funcionen juntos de manera coherente como una familia.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"

using namespace std;

int main() {
    // Leer entradas
    string documentType;
    string format;
    cin >> documentType;
    cin >> format;
    
    // TODO: Usar la función de fábrica simple para crear un documento
    // basado en las entradas documentType y format
    // Imprimir el resultado de llamar a generate() en él
    
    // Imprimir una línea en blanco
    cout << endl;
    
    // TODO: Crear un DocumentService con la fábrica apropiada
    // basado en la entrada format (PDFFactory para "pdf", HTMLFactory para "html")
    // Llamar a generateAllDocuments() para demostrar la fábrica abstracta
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos