Factory e Abstract Factory
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 92 di 104.
Il pattern Factory delega la creazione degli oggetti a un metodo o a una classe separati, consentendoti di creare oggetti senza specificare la loro classe esatta. È utile quando la logica di creazione è complessa o quando vuoi disaccoppiare il codice client dalle implementazioni concrete.
Una factory semplice usa un metodo che restituisce tipi derivati diversi in base all'input:
#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"; }
};
// Funzione 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;
}La Abstract Factory va oltre, creando famiglie di oggetti correlati. Invece di un solo metodo factory, hai un'interfaccia factory con più metodi di creazione:
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"; }
};
// Fabbrica astratta
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 quando hai bisogno di una creazione flessibile di oggetti e Abstract Factory quando devi assicurarti che gli oggetti correlati funzionino insieme in modo coerente.
Sfida
FacileCostruiamo un sistema di generazione di documenti usando i pattern Factory e Abstract Factory. Creerai un sistema in grado di generare diversi tipi di documenti (report e fatture) in diversi formati di output (PDF e HTML), mostrando come le factory disaccoppiano la creazione degli oggetti dal codice client.
Organizzerai il codice in quattro file:
Document.h: definisci la gerarchia dei prodotti documentali.Crea una classe base astratta
Documentcon un metodo virtuale purogenerate()che restituisce una stringa che descrive il documento generato, e un distruttore virtuale.Crea poi quattro classi concrete per i documenti:
PDFReport:generate()restituisce"Generating PDF Report"HTMLReport:generate()restituisce"Generating HTML Report"PDFInvoice:generate()restituisce"Generating PDF Invoice"HTMLInvoice:generate()restituisce"Generating HTML Invoice"
DocumentFactory.h: implementa sia una funzione factory semplice sia una factory astratta.Per prima cosa, crea una funzione factory semplice chiamata
createDocumentche accetta due stringhe: un tipo di documento ("report"o"invoice") e un formato ("pdf"o"html"). Dovrebbe restituire unostd::unique_ptr<Document>che punta al documento concreto appropriato. Restituiscinullptrper le combinazioni non valide.Crea poi una classe factory astratta chiamata
DocumentFactorycon due metodi virtuali puri:createReport(): restituisce unostd::unique_ptr<Document>createInvoice(): restituisce unostd::unique_ptr<Document>
Implementa due factory concrete:
PDFFactory: creaPDFReportePDFInvoiceHTMLFactory: creaHTMLReporteHTMLInvoice
DocumentService.h: crea un servizio che usa la factory astratta.Definisci una classe
DocumentServiceche accetta unostd::unique_ptr<DocumentFactory>nel costruttore. Dovrebbe avere un metodo chiamatogenerateAllDocuments()che usa la factory per creare sia un report sia una fattura, quindi stampa il risultato della chiamata agenerate()su ciascuno, uno per riga.main.cpp: mostra entrambi gli approcci factory.Leggi due valori in ingresso:
- Un tipo di documento (
reportoinvoice) - Un formato (
pdfohtml)
Per prima cosa, usa la funzione factory semplice per creare un documento in base ai valori inseriti. Stampa il risultato della chiamata a
generate()su di esso.Stampa poi una riga vuota.
Successivamente, crea un
DocumentServicecon la factory appropriata in base al formato inserito (usaPDFFactoryperpdf,HTMLFactoryperhtml). ChiamagenerateAllDocuments()per mostrare come la factory astratta crea una famiglia di documenti correlati.- Un tipo di documento (
Ad esempio, con gli input report e pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceCon gli input invoice e html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceNota come la factory semplice crei un singolo documento in base a entrambi i parametri, mentre la factory astratta garantisce che tutti i documenti creati da un servizio condividano lo stesso formato. Questo è il vantaggio principale di Abstract Factory quando hai bisogno di famiglie di oggetti correlati che funzionino insieme in modo coerente.
Provalo tu
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// Leggi gli input
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: Usa la funzione factory semplice per creare un documento
// in base agli input documentType e format
// Stampa il risultato della chiamata a generate() sul documento
// Stampa una riga vuota
cout << endl;
// TODO: Crea un DocumentService con la factory appropriata
// in base all'input format (PDFFactory per "pdf", HTMLFactory per "html")
// Chiama generateAllDocuments() per mostrare il funzionamento della factory astratta
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti10Panoramica della STL
Panoramica e filosofia della STLContenitori STLIteratoriAlgoritmi STLFuntori ed espressioni lambdaRiepilogo - Frequenza delle parole13Design Pattern Parte 1
Introduzione ai Design PatternPattern SingletonFactory e Abstract FactoryPattern BuilderPattern ObserverPattern Strategy2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online