Factory & Abstrakte Fabrik
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 92 von 104.
Das Factory-Pattern delegiert die Objekterzeugung an eine separate Methode oder Klasse, sodass Sie Objekte erstellen können, ohne deren genaue Klasse anzugeben. Dies ist nützlich, wenn die Erzeugungslogik komplex ist oder wenn Sie den Client-Code von konkreten Implementierungen entkoppeln möchten.
Eine einfache Factory verwendet eine Methode, die basierend auf der Eingabe verschiedene abgeleitete Typen zurückgibt:
#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"; }
};
// Factory-Funktion
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;
}Die Abstrakte Fabrik (Abstract Factory) geht noch einen Schritt weiter, indem sie Familien verwandter Objekte erstellt. Anstelle einer einzelnen Fabrikmethode haben Sie eine Fabrikschnittstelle mit mehreren Erstellungsmethoden:
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"; }
};
// Abstrakte Fabrik
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>();
}
};Verwenden Sie Factory, wenn Sie eine flexible Objekterstellung benötigen, und Abstract Factory, wenn Sie sicherstellen müssen, dass zusammengehörige Objekte konsistent zusammenarbeiten.
Aufgabe
EinfachLassen Sie uns ein Document Generator System unter Verwendung der Factory- und Abstract Factory-Muster erstellen. Sie werden ein System entwickeln, das verschiedene Arten von Dokumenten (Berichte und Rechnungen) für unterschiedliche Ausgabeformate (PDF und HTML) generieren kann, und dabei demonstrieren, wie Factories die Objekterstellung vom Client-Code entkoppeln.
Sie werden Ihren Code über vier Dateien organisieren:
Document.h: Definieren Sie Ihre Dokument-Produkthierarchie.Erstellen Sie eine abstrakte Basisklasse
Documentmit einer rein virtuellen Methodegenerate(), die einen String zurückgibt, der das generierte Dokument beschreibt, sowie einen virtuellen Destruktor.Erstellen Sie dann vier konkrete Dokumentklassen:
PDFReport—generate()gibt"Generating PDF Report"zurückHTMLReport—generate()gibt"Generating HTML Report"zurückPDFInvoice—generate()gibt"Generating PDF Invoice"zurückHTMLInvoice—generate()gibt"Generating HTML Invoice"zurück
DocumentFactory.h: Implementieren Sie sowohl eine einfache Factory-Funktion als auch eine Abstract Factory.Erstellen Sie zuerst eine einfache Factory-Funktion namens
createDocument, die zwei Strings entgegennimmt: einen Dokumenttyp ("report"oder"invoice") und ein Format ("pdf"oder"html"). Sie sollte einstd::unique_ptr<Document>zurückgeben, das auf das entsprechende konkrete Dokument zeigt. Geben Sienullptrfür ungültige Kombinationen zurück.Erstellen Sie dann eine abstrakte Factory-Klasse namens
DocumentFactorymit zwei rein virtuellen Methoden:createReport()— gibt einstd::unique_ptr<Document>zurückcreateInvoice()— gibt einstd::unique_ptr<Document>zurück
Implementieren Sie zwei konkrete Factories:
PDFFactory— erstelltPDFReportundPDFInvoiceHTMLFactory— erstelltHTMLReportundHTMLInvoice
DocumentService.h: Erstellen Sie einen Service, der die Abstract Factory verwendet.Definieren Sie eine
DocumentService-Klasse, die einstd::unique_ptr<DocumentFactory>in ihrem Konstruktor entgegennimmt. Sie sollte eine Methode namensgenerateAllDocuments()haben, die die Factory verwendet, um sowohl einen Bericht als auch eine Rechnung zu erstellen, und dann das Ergebnis des Aufrufs vongenerate()für jedes Dokument ausgibt, eines pro Zeile.main.cpp: Demonstrieren Sie beide Factory-Ansätze.Lesen Sie zwei Eingaben ein:
- Einen Dokumenttyp (
reportoderinvoice) - Ein Format (
pdfoderhtml)
Verwenden Sie zuerst die einfache Factory-Funktion, um ein Dokument basierend auf den Eingaben zu erstellen. Geben Sie das Ergebnis des Aufrufs von
generate()darauf aus.Geben Sie dann eine Leerzeile aus.
Erstellen Sie als Nächstes einen
DocumentServicemit der entsprechenden Factory basierend auf der Formateingabe (verwenden SiePDFFactoryfürpdf,HTMLFactoryfürhtml). Rufen SiegenerateAllDocuments()auf, um zu zeigen, wie die Abstract Factory eine Familie verwandter Dokumente erstellt.- Einen Dokumenttyp (
Zum Beispiel mit den Eingaben report und pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceMit den Eingaben invoice und html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceBeachten Sie, wie die einfache Factory ein einzelnes Dokument basierend auf beiden Parametern erstellt, während die Abstract Factory sicherstellt, dass alle von einem Service erstellten Dokumente dasselbe Format teilen – dies ist der Hauptvorteil der Abstract Factory, wenn Sie Familien verwandter Objekte benötigen, die konsistent zusammenarbeiten.
Spickzettel
Das Factory-Muster delegiert die Objekterstellung an eine separate Methode oder Klasse, was es Ihnen ermöglicht, Objekte zu erstellen, ohne deren genaue Klasse anzugeben. Dies entkoppelt den Client-Code von konkreten Implementierungen.
Eine einfache Fabrik verwendet eine Funktion, die basierend auf der Eingabe verschiedene abgeleitete Typen zurückgibt:
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-Funktion
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;
}Das Abstract Factory-Muster erstellt Familien verwandter Objekte. Anstelle einer einzelnen Factory-Methode verfügen Sie über ein Factory-Interface mit mehreren Erstellungsmethoden:
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"; }
};
// Abstrakte Fabrik
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>();
}
};Verwenden Sie die Factory, wenn Sie eine flexible Objekterstellung benötigen. Verwenden Sie die Abstract Factory, wenn Sie sicherstellen müssen, dass verwandte Objekte konsistent als Familie zusammenarbeiten.
Probier es selbst
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// Eingaben lesen
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: Verwende die einfache Factory-Funktion, um ein Dokument zu erstellen
// basierend auf den Eingaben documentType und format
// Gib das Ergebnis des Aufrufs von generate() darauf aus
// Eine Leerzeile ausgeben
cout << endl;
// TODO: Erstelle einen DocumentService mit der entsprechenden Factory
// basierend auf der Eingabe format (PDFFactory für "pdf", HTMLFactory für "html")
// Rufe generateAllDocuments() auf, um die abstrakte Factory zu demonstrieren
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Wortfrequenz13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory & Abstrakte FabrikBuilder-MusterObserver-MusterStrategy-Muster2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container