Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Erstellen 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 Document mit einer rein virtuellen Methode generate(), 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ück
    • HTMLReport: generate() gibt "Generating HTML Report" zurück
    • PDFInvoice: generate() gibt "Generating PDF Invoice" zurück
    • HTMLInvoice: 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 einen std::unique_ptr<Document> zurückgeben, der auf das entsprechende konkrete Dokument zeigt. Gib bei ungültigen Kombinationen nullptr zurück.

    Erstelle anschließend eine Abstract-Factory-Klasse namens DocumentFactory mit zwei rein virtuellen Methoden:

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

    Implementiere zwei konkrete Factories:

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

    Definiere eine Klasse DocumentService, die in ihrem Konstruktor ein std::unique_ptr<DocumentFactory> entgegennimmt. Sie sollte eine Methode namens generateAllDocuments() 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 von generate() ausgibt, jeweils in einer eigenen Zeile.

  • main.cpp: Zeige beide Factory-Ansätze.

    Lies zwei Eingaben ein:

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

    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 DocumentService mit der passenden Factory, basierend auf der Formateingabe (verwende PDFFactory für pdf und HTMLFactory für html). Rufe generateAllDocuments() auf, um zu zeigen, wie die Abstract Factory eine Familie zusammengehöriger Dokumente erstellt.

Bei den Eingaben report und pdf erhältst du beispielsweise:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

Bei den Eingaben invoice und html erhältst du:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

Beachte, 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;
}
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

Übe selbstständig: Online-C++-Compiler