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 una clase independientes, lo que permite crear objetos sin especificar su clase exacta. Esto resulta útil cuando la lógica de creación es compleja o cuando quieres desacoplar el código del cliente de las implementaciones concretas.
Una fábrica simple utiliza un método que devuelve diferentes tipos derivados según 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;
}La fábrica abstracta lleva esto más lejos al crear familias de objetos relacionados. En lugar de un único 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 flexible de objetos y Abstract Factory cuando necesites garantizar que los objetos relacionados funcionen juntos de manera coherente.
Desafío
FácilConstruyamos un sistema de Generación de documentos utilizando los patrones Factory y Abstract Factory. Crearás un sistema capaz de generar distintos 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 cliente.
Organizarás tu código en cuatro archivos:
Document.h: Define la jerarquía de productos de documentos.Crea una clase base abstracta
Documentcon un método virtual purogenerate()que devuelva una cadena que describa el documento generado, y un destructor virtual.Después 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 cadenas: un tipo de documento ("report"o"invoice") y un formato ("pdf"o"html"). Debe devolver unstd::unique_ptr<Document>que apunte al documento concreto correspondiente. Devuelvenullptrpara combinaciones no válidas.Después 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, después, 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 según las entradas. Imprime el resultado de llamar a
generate()en él.Después imprime una línea en blanco.
A continuación, crea un
DocumentServicecon la fábrica adecuada según la entrada de formato (usaPDFFactoryparapdfyHTMLFactoryparahtml). 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 único documento basándose en ambos parámetros, mientras que la fábrica abstracta garantiza que todos los documentos creados por un servicio compartan el mismo formato. Este es el principal beneficio de Abstract Factory cuando necesitas familias de objetos relacionados que funcionen juntas de manera coherente.
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: Usa la función de fábrica simple para crear un documento
// basado en las entradas documentType y format
// Imprime el resultado de llamar a generate() en él
// Imprime una línea en blanco
cout << endl;
// TODO: Crea un DocumentService con la fábrica apropiada
// basado en la entrada format (PDFFactory para "pdf", HTMLFactory para "html")
// Llama 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éricoPractica por tu cuenta: Compilador de C++ online