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.
Desafío
FácilVamos 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
Documentcon un método virtual purogenerate()que devuelva un string describiendo el documento generado, y un destructor virtual.Luego crea cuatro clases de documentos concretas:
PDFReport—generate()devuelve"Generating PDF Report"HTMLReport—generate()devuelve"Generating HTML Report"PDFInvoice—generate()devuelve"Generating PDF Invoice"HTMLInvoice—generate()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
createDocumentque reciba dos strings: un tipo de documento ("report"o"invoice") y un formato ("pdf"o"html"). Debe devolver unstd::unique_ptr<Document>que apunte al documento concreto apropiado. Devuelvenullptrpara combinaciones inválidas.Luego crea una clase de fábrica abstracta llamada
DocumentFactorycon dos métodos virtuales puros:createReport()— devuelve unstd::unique_ptr<Document>createInvoice()— devuelve unstd::unique_ptr<Document>
Implementa dos fábricas concretas:
PDFFactory— creaPDFReportyPDFInvoiceHTMLFactory— creaHTMLReportyHTMLInvoice
DocumentService.h: Crea un servicio que utilice la fábrica abstracta.Define una clase
DocumentServiceque reciba unstd::unique_ptr<DocumentFactory>en su constructor. Debe tener un método llamadogenerateAllDocuments()que utilice la fábrica para crear tanto un informe como una factura, y luego imprima el resultado de llamar agenerate()en cada uno, uno por línea.main.cpp: Demuestra ambos enfoques de fábrica.Lee dos entradas:
- Un tipo de documento (
reportoinvoice) - Un formato (
pdfohtml)
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
DocumentServicecon la fábrica apropiada basada en la entrada del formato (usaPDFFactoryparapdf,HTMLFactoryparahtml). Llama agenerateAllDocuments()para mostrar cómo la fábrica abstracta crea una familia de documentos relacionados.- Un tipo de documento (
Por ejemplo, con las entradas report y pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceCon las entradas invoice y html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceObserva 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;
}
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
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados10Visión general de la STL
Visión general y filosofía de la STLContenedores de la STLIteradoresAlgoritmos de la STLFuntores y expresiones lambdaResumen - Frecuencia de palabras13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonFactory y Abstract FactoryPatrón BuilderPatrón ObserverPatrón Strategy2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico