Builder-Muster
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 93 von 104.
Das Builder-Pattern trennt die Konstruktion eines komplexen Objekts von seiner Repräsentation, sodass Sie verschiedene Konfigurationen Schritt für Schritt erstellen können. Dies ist besonders nützlich, wenn ein Objekt viele optionale Parameter hat oder eine bestimmte Konstruktionsreihenfolge erfordert.
Anstelle eines Konstruktors mit vielen Parametern (was unübersichtlich wird), bietet der Builder klare, benannte Methoden für jede Konfigurationsoption:
#include <iostream>
#include <string>
class Pizza {
public:
std::string dough;
std::string sauce;
std::string topping;
bool cheese;
void describe() const {
std::cout << dough << " dough, " << sauce << " sauce, "
<< topping << (cheese ? ", with cheese" : "") << "\n";
}
};
class PizzaBuilder {
private:
Pizza pizza;
public:
PizzaBuilder& setDough(const std::string& d) {
pizza.dough = d;
return *this;
}
PizzaBuilder& setSauce(const std::string& s) {
pizza.sauce = s;
return *this;
}
PizzaBuilder& setTopping(const std::string& t) {
pizza.topping = t;
return *this;
}
PizzaBuilder& addCheese() {
pizza.cheese = true;
return *this;
}
Pizza build() { return pizza; }
};
int main() {
Pizza margherita = PizzaBuilder()
.setDough("thin")
.setSauce("tomato")
.setTopping("basil")
.addCheese()
.build();
margherita.describe();
}Jede Setter-Methode gibt eine Referenz auf den Builder zurück (return *this), was Method Chaining für ein Fluent Interface ermöglicht. Die build()-Methode schließt den Vorgang ab und gibt das konstruierte Objekt zurück.
Verwenden Sie Builder beim Erstellen von Objekten mit vielen optionalen Komponenten, wenn Sie lesbaren Konstruktionscode wünschen oder wenn derselbe Konstruktionsprozess verschiedene Darstellungen erzeugen soll.
Aufgabe
EinfachLassen Sie uns ein Computer Builder-System erstellen, mit dem Benutzer benutzerdefinierte PCs Schritt für Schritt konfigurieren können. Dies ist ein perfektes Szenario für das Builder-Muster – Computer haben viele optionale Komponenten, und wir möchten eine saubere, lesbare Möglichkeit, verschiedene Konfigurationen zusammenzustellen.
Sie werden Ihren Code über drei Dateien hinweg organisieren:
Computer.h: Definieren Sie die Produktklasse, die einen vollständig konfigurierten Computer darstellt.Ihre
Computer-Klasse sollte diese Komponenten als private Member speichern:cpu(string),ram(string),storage(string),gpu(string) undhasWifi(boolean). Initialisieren SiehasWifiauffalseund die Strings standardmäßig als leer.Fügen Sie eine öffentliche Methode namens
showSpecs()hinzu, die die Konfiguration des Computers anzeigt. Geben Sie für jede Komponente, die gesetzt wurde (nicht leerer String), diese in einer eigenen Zeile aus. Die WLAN-Funktion sollte nur ausgegeben werden, wenn sie aktiviert ist.ComputerBuilder.h: Erstellen Sie die Builder-Klasse, die Computer mit einem Fluent Interface konstruiert.Ihre
ComputerBuilder-Klasse sollte ein privatesComputer-Member haben, das sie aufbaut. Implementieren Sie diese Methoden, die jeweils eine Referenz auf den Builder zurückgeben, um Method Chaining zu ermöglichen:setCPU(const std::string& cpu)setRAM(const std::string& ram)setStorage(const std::string& storage)setGPU(const std::string& gpu)addWifi()— aktiviert die WLAN-Funktion
Implementieren Sie außerdem eine
build()-Methode, die das fertigeComputer-Objekt zurückgibt und den Builder für eine potenzielle Wiederverwendung zurücksetzt.main.cpp: Bringen Sie alles zusammen, um einen benutzerdefinierten Computer zu bauen.Lesen Sie vier Eingaben ein:
- CPU-Modell (string)
- RAM-Spezifikation (string)
- Speicher-Spezifikation (string)
- Ob WLAN enthalten sein soll:
yesoderno
Verwenden Sie den
ComputerBuilder, um einen Computer mit der angegebenen CPU, dem RAM und dem Speicher zu konstruieren. Wenn die WLAN-Eingabeyesist, rufen Sie zusätzlichaddWifi()auf. Beachten Sie, dass in dieser Konfiguration keine GPU angegeben ist – der Builder sollte optionale Komponenten reibungslos verarbeiten.Rufen Sie nach dem Erstellen
showSpecs()auf dem resultierenden Computer auf, um seine Konfiguration anzuzeigen.
Die Methode showSpecs() sollte in diesem Format ausgeben (nur Komponenten anzeigen, die gesetzt wurden):
CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: EnabledZum Beispiel mit den Eingaben Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD und yes:
CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: EnabledMit den Eingaben AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD und no:
CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSDBeachten Sie, wie das Builder-Muster den Konstruktionsprozess lesbar und flexibel macht – Sie können Komponenten einfach hinzufügen oder überspringen, und das Method Chaining erzeugt klaren, selbstdokumentierenden Code. Die GPU-Zeile erscheint nicht, weil wir sie nie gesetzt haben, was zeigt, wie Builder optionale Komponenten natürlich handhaben.
Spickzettel
Das Builder-Muster trennt die Konstruktion eines komplexen Objekts von seiner Darstellung und ermöglicht so eine schrittweise Konfiguration. Es ist nützlich, wenn ein Objekt viele optionale Parameter hat oder eine bestimmte Konstruktionsreihenfolge erfordert.
Anstelle von Konstruktoren mit vielen Parametern bietet der Builder klare, benannte Methoden für jede Konfigurationsoption:
class Pizza {
public:
std::string dough;
std::string sauce;
std::string topping;
bool cheese;
};
class PizzaBuilder {
private:
Pizza pizza;
public:
PizzaBuilder& setDough(const std::string& d) {
pizza.dough = d;
return *this; // Methodenverkettung ermöglichen
}
PizzaBuilder& setSauce(const std::string& s) {
pizza.sauce = s;
return *this;
}
PizzaBuilder& setTopping(const std::string& t) {
pizza.topping = t;
return *this;
}
PizzaBuilder& addCheese() {
pizza.cheese = true;
return *this;
}
Pizza build() { return pizza; }
};
// Verwendung mit Methodenverkettung
Pizza margherita = PizzaBuilder()
.setDough("thin")
.setSauce("tomato")
.setTopping("basil")
.addCheese()
.build();Jede Setter-Methode gibt return *this zurück, um die Methodenverkettung für ein Fluent Interface zu ermöglichen. Die Methode build() schließt den Vorgang ab und gibt das konstruierte Objekt zurück.
Verwenden Sie den Builder beim Erstellen von Objekten mit vielen optionalen Komponenten, wenn Sie lesbaren Konstruktionscode wünschen oder wenn derselbe Konstruktionsprozess unterschiedliche Darstellungen erzeugen soll.
Probier es selbst
#include <iostream>
#include <string>
#include "ComputerBuilder.h"
using namespace std;
int main() {
// Eingaben lesen
string cpu, ram, storage, wifiChoice;
getline(cin, cpu);
getline(cin, ram);
getline(cin, storage);
getline(cin, wifiChoice);
// TODO: Eine ComputerBuilder-Instanz erstellen
// TODO: Method Chaining verwenden, um CPU, RAM und storage festzulegen
// TODO: Wenn wifiChoice "yes" ist, auch addWifi() aufrufen
// TODO: build() aufrufen, um das Computer-Objekt zu erhalten
// TODO: showSpecs() auf dem erstellten Computer aufrufen, um die Konfiguration anzuzeigen
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 & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Wortfrequenz13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory & Abstrakte FabrikBuilder-MusterObserver-MusterStrategy-Muster2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container