Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 Document mit einer rein virtuellen Methode generate(), die einen String zurückgibt, der das generierte Dokument beschreibt, sowie einen virtuellen Destruktor.

    Erstellen Sie dann vier konkrete Dokumentklassen:

    • PDFReportgenerate() gibt "Generating PDF Report" zurück
    • HTMLReportgenerate() gibt "Generating HTML Report" zurück
    • PDFInvoicegenerate() gibt "Generating PDF Invoice" zurück
    • HTMLInvoicegenerate() 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 ein std::unique_ptr<Document> zurückgeben, das auf das entsprechende konkrete Dokument zeigt. Geben Sie nullptr für ungültige Kombinationen zurück.

    Erstellen Sie dann eine abstrakte Factory-Klasse namens DocumentFactory mit zwei rein virtuellen Methoden:

    • createReport() — gibt ein std::unique_ptr<Document> zurück
    • createInvoice() — gibt ein std::unique_ptr<Document> zurück

    Implementieren Sie zwei konkrete Factories:

    • PDFFactory — erstellt PDFReport und PDFInvoice
    • HTMLFactory — erstellt HTMLReport und HTMLInvoice
  • DocumentService.h: Erstellen Sie einen Service, der die Abstract Factory verwendet.

    Definieren Sie eine DocumentService-Klasse, die ein std::unique_ptr<DocumentFactory> in ihrem Konstruktor entgegennimmt. Sie sollte eine Methode namens generateAllDocuments() haben, die die Factory verwendet, um sowohl einen Bericht als auch eine Rechnung zu erstellen, und dann das Ergebnis des Aufrufs von generate() für jedes Dokument ausgibt, eines pro Zeile.

  • main.cpp: Demonstrieren Sie beide Factory-Ansätze.

    Lesen Sie zwei Eingaben ein:

    1. Einen Dokumenttyp (report oder invoice)
    2. Ein Format (pdf oder html)

    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 DocumentService mit der entsprechenden Factory basierend auf der Formateingabe (verwenden Sie PDFFactory für pdf, HTMLFactory für html). Rufen Sie generateAllDocuments() auf, um zu zeigen, wie die Abstract Factory eine Familie verwandter Dokumente erstellt.

Zum Beispiel mit den Eingaben report und pdf:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

Mit den Eingaben invoice und html:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

Beachten 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;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung