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ć.
Wyzwanie
ŁatwyZbudujmy 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ą
Documentz 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źnikstd::unique_ptr<Document>wskazujący na odpowiedni konkretny dokument. Dla nieprawidłowych kombinacji zwróćnullptr.Następnie utwórz klasę fabryki abstrakcyjnej o nazwie
DocumentFactoryz dwiema czysto wirtualnymi metodami:createReport(): zwracastd::unique_ptr<Document>createInvoice(): zwracastd::unique_ptr<Document>
Zaimplementuj dwie konkretne fabryki:
PDFFactory: tworzyPDFReportiPDFInvoiceHTMLFactory: tworzyHTMLReportiHTMLInvoice
DocumentService.h: Utwórz usługę korzystającą z fabryki abstrakcyjnej.Zdefiniuj klasę
DocumentService, która w konstruktorze przyjmujestd::unique_ptr<DocumentFactory>. Powinna mieć metodę o nazwiegenerateAllDocuments(), która używa fabryki do utworzenia zarówno raportu, jak i faktury, a następnie wypisuje wynik wywołaniagenerate()dla każdego z nich, po jednym wierszu.main.cpp: Pokaż oba podejścia z użyciem fabryk.Wczytaj dwa dane wejściowe:
- Typ dokumentu (
reportlubinvoice) - Format (
pdflubhtml)
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
DocumentServicez odpowiednią fabryką na podstawie podanego formatu (użyjPDFFactorydlapdf,HTMLFactorydlahtml). WywołajgenerateAllDocuments(), aby pokazać, jak fabryka abstrakcyjna tworzy rodzinę powiązanych dokumentów.- Typ dokumentu (
Na przykład dla danych wejściowych report i pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceDla danych wejściowych invoice i html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceZwróć 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;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników10Przegląd STL
Przegląd i filozofia STLKontenery STLIteratoryAlgorytmy STLFunktory i wyrażenia lambdaPowtórzenie – częstość występowania słów13Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonFabryka i fabryka abstrakcyjnaWzorzec BuilderWzorzec ObserverWzorzec Strategy2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online