Factory und Abstract Factory
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 92 von 104.
Das Factory-Muster überträgt die Objekterstellung an eine separate Methode oder Klasse, sodass du Objekte erstellen kannst, ohne ihre genaue Klasse anzugeben. Dies ist nützlich, wenn die Erstellungslogik komplex ist oder wenn du den Client-Code von konkreten Implementierungen entkoppeln möchtest.
Eine einfache Factory verwendet eine Methode, die abhängig von 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"; }
};
// Fabrikfunktion
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 geht noch einen Schritt weiter, indem sie Familien verwandter Objekte erstellt. Anstelle einer einzelnen Fabrikmethode hast du 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>();
}
};Verwende Factory, wenn du eine flexible Objekterstellung benötigst, und Abstract Factory, wenn du sicherstellen musst, dass zusammengehörige Objekte konsistent zusammenarbeiten.
Aufgabe
EinfachErstellen wir ein Dokumentgenerierungssystem mithilfe der Factory- und Abstract-Factory-Muster. Du wirst ein System erstellen, das verschiedene Dokumenttypen (Berichte und Rechnungen) für verschiedene Ausgabeformate (PDF und HTML) generieren kann und dabei zeigt, wie Factories die Objekterstellung vom Client-Code entkoppeln.
Du wirst deinen Code auf vier Dateien verteilen:
Document.h: Definiere deine Produkthierarchie für Dokumente.Erstelle eine abstrakte Basisklasse
Documentmit einer rein virtuellen Methodegenerate(), die eine Zeichenkette zurückgibt, die das generierte Dokument beschreibt, sowie einem virtuellen Destruktor.Erstelle anschließend 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: Implementiere sowohl eine einfache Factory-Funktion als auch eine Abstract Factory.Erstelle zunächst eine einfache Factory-Funktion namens
createDocument, die zwei Zeichenketten entgegennimmt: einen Dokumenttyp ("report"oder"invoice") und ein Format ("pdf"oder"html"). Sie sollte einenstd::unique_ptr<Document>zurückgeben, der auf das entsprechende konkrete Dokument zeigt. Gib bei ungültigen Kombinationennullptrzurück.Erstelle anschließend eine Abstract-Factory-Klasse namens
DocumentFactorymit zwei rein virtuellen Methoden:createReport(): gibt einenstd::unique_ptr<Document>zurückcreateInvoice(): gibt einenstd::unique_ptr<Document>zurück
Implementiere zwei konkrete Factories:
PDFFactory: erstelltPDFReportundPDFInvoiceHTMLFactory: erstelltHTMLReportundHTMLInvoice
DocumentService.h: Erstelle einen Service, der die Abstract Factory verwendet.Definiere eine Klasse
DocumentService, die in ihrem Konstruktor einstd::unique_ptr<DocumentFactory>entgegennimmt. Sie sollte eine Methode namensgenerateAllDocuments()besitzen, die die Factory verwendet, um sowohl einen Bericht als auch eine Rechnung zu erstellen, und anschließend für jedes Dokument das Ergebnis des Aufrufs vongenerate()ausgibt, jeweils in einer eigenen Zeile.main.cpp: Zeige beide Factory-Ansätze.Lies zwei Eingaben ein:
- Einen Dokumenttyp (
reportoderinvoice) - Ein Format (
pdfoderhtml)
Verwende zunächst die einfache Factory-Funktion, um anhand der Eingaben ein Dokument zu erstellen. Gib das Ergebnis des Aufrufs von
generate()darauf aus.Gib anschließend eine leere Zeile aus.
Erstelle danach einen
DocumentServicemit der passenden Factory, basierend auf der Formateingabe (verwendePDFFactoryfürpdfundHTMLFactoryfürhtml). RufegenerateAllDocuments()auf, um zu zeigen, wie die Abstract Factory eine Familie zusammengehöriger Dokumente erstellt.- Einen Dokumenttyp (
Bei den Eingaben report und pdf erhältst du beispielsweise:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceBei den Eingaben invoice und html erhältst du:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceBeachte, wie die einfache Factory ein einzelnes Dokument anhand beider Parameter erstellt, während die Abstract Factory sicherstellt, dass alle von einem Service erstellten Dokumente dasselbe Format haben. Das ist der entscheidende Vorteil der Abstract Factory, wenn du Familien zusammengehöriger Objekte benötigst, die konsistent 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 documentType- und format-Eingaben
// Gib das Ergebnis des Aufrufs von generate() darauf aus
// Gib eine leere Zeile aus
cout << endl;
// TODO: Erstelle einen DocumentService mit der entsprechenden Factory
// basierend auf der format-Eingabe (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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Worthäufigkeit13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory und Abstract FactoryBuilder-MusterBeobachter-MusterStrategie-Muster2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler