Menu
Coddy logo textTech

Fabryka i fabryka abstrakcyjna

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 92 z 104.

Wzorzec fabryki deleguje tworzenie obiektów do osobnej metody lub klasy, umożliwiając tworzenie obiektów bez określania ich dokładnej klasy. Jest to przydatne, gdy logika tworzenia jest złożona lub gdy chcesz oddzielić kod klienta od konkretnych implementacji.

Prosta fabryka używa metody, która zwraca różne typy pochodne na podstawie danych wejściowych:

#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"; }
};

// Funkcja fabrykująca
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;
}

Fabryka abstrakcyjna idzie o krok dalej, tworząc rodziny powiązanych ze sobą obiektów. Zamiast jednej metody fabrykującej masz interfejs fabryki z wieloma metodami tworzenia obiektów:

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"; }
};

// Fabryka abstrakcyjna
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>();
    }
};

Użyj wzorca Factory, gdy potrzebujesz elastycznego tworzenia obiektów, a wzorca Abstract Factory, gdy chcesz mieć pewność, że powiązane obiekty będą ze sobą spójnie współpracować.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system generatora dokumentów, wykorzystując wzorce Fabryki i Fabryki abstrakcyjnej. Stworzysz system, który może generować różne typy dokumentów (raporty i faktury) w różnych formatach wyjściowych (PDF i HTML), pokazując, jak fabryki oddzielają tworzenie obiektów od kodu klienta.

Rozmieścisz kod w czterech plikach:

  • Document.h: Zdefiniuj hierarchię produktów dokumentów.

    Utwórz abstrakcyjną klasę bazową Document z czysto wirtualną metodą generate(), która zwraca ciąg znaków opisujący wygenerowany dokument, oraz wirtualnym destruktorem.

    Następnie utwórz cztery konkretne klasy dokumentów:

    • PDFReport: generate() zwraca "Generating PDF Report"
    • HTMLReport: generate() zwraca "Generating HTML Report"
    • PDFInvoice: generate() zwraca "Generating PDF Invoice"
    • HTMLInvoice: generate() zwraca "Generating HTML Invoice"
  • DocumentFactory.h: Zaimplementuj zarówno prostą funkcję fabrykującą, jak i fabrykę abstrakcyjną.

    Najpierw utwórz prostą funkcję fabrykującą o nazwie createDocument, która przyjmuje dwa ciągi znaków: typ dokumentu ("report" lub "invoice") oraz format ("pdf" lub "html"). Powinna zwracać wskaźnik std::unique_ptr<Document> wskazujący na odpowiedni konkretny dokument. Dla nieprawidłowych kombinacji zwróć nullptr.

    Następnie utwórz klasę fabryki abstrakcyjnej o nazwie DocumentFactory z dwiema czysto wirtualnymi metodami:

    • createReport(): zwraca std::unique_ptr<Document>
    • createInvoice(): zwraca std::unique_ptr<Document>

    Zaimplementuj dwie konkretne fabryki:

    • PDFFactory: tworzy PDFReport i PDFInvoice
    • HTMLFactory: tworzy HTMLReport i HTMLInvoice
  • DocumentService.h: Utwórz usługę korzystającą z fabryki abstrakcyjnej.

    Zdefiniuj klasę DocumentService, która w konstruktorze przyjmuje std::unique_ptr<DocumentFactory>. Powinna mieć metodę o nazwie generateAllDocuments(), która używa fabryki do utworzenia zarówno raportu, jak i faktury, a następnie wypisuje wynik wywołania generate() dla każdego z nich, po jednym wierszu.

  • main.cpp: Pokaż oba podejścia z użyciem fabryk.

    Wczytaj dwa dane wejściowe:

    1. Typ dokumentu (report lub invoice)
    2. Format (pdf lub html)

    Najpierw użyj prostej funkcji fabrykującej, aby utworzyć dokument na podstawie danych wejściowych. Wypisz wynik wywołania generate() dla tego dokumentu.

    Następnie wypisz pusty wiersz.

    Potem utwórz DocumentService z odpowiednią fabryką na podstawie podanego formatu (użyj PDFFactory dla pdf, HTMLFactory dla html). Wywołaj generateAllDocuments(), aby pokazać, jak fabryka abstrakcyjna tworzy rodzinę powiązanych dokumentów.

Na przykład dla danych wejściowych report i pdf:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

Dla danych wejściowych invoice i html:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

Zwróć uwagę, że prosta fabryka tworzy pojedynczy dokument na podstawie obu parametrów, podczas gdy fabryka abstrakcyjna zapewnia, że wszystkie dokumenty tworzone przez usługę mają ten sam format. To główna zaleta wzorca Fabryki abstrakcyjnej, gdy potrzebujesz rodzin powiązanych obiektów, które współpracują ze sobą w spójny sposób.

Spróbuj swoich sił

#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"

using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string documentType;
    string format;
    cin >> documentType;
    cin >> format;
    
    // TODO: Użyj funkcji prostej fabryki, aby utworzyć dokument
    // na podstawie danych wejściowych documentType i format
    // Wypisz wynik wywołania na nim generate()
    
    // Wypisz pustą linię
    cout << endl;
    
    // TODO: Utwórz DocumentService z odpowiednią fabryką
    // na podstawie wartości wejściowej format (PDFFactory dla "pdf", HTMLFactory dla "html")
    // Wywołaj generateAllDocuments(), aby zademonstrować fabrykę abstrakcyjną
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online