Builder-Muster
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 93 von 104.
Das Builder-Muster trennt die Konstruktion eines komplexen Objekts von seiner Darstellung und ermöglicht es dir, verschiedene Konfigurationen Schritt für Schritt zu erstellen. Dies ist besonders nützlich, wenn ein Objekt viele optionale Parameter hat oder eine bestimmte Konstruktionsreihenfolge erfordert.
Anstelle eines Konstruktors mit vielen Parametern (der unübersichtlich wird) stellt der Builder klare, benannte Methoden für jede Konfigurationsoption bereit:
#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), wodurch das Verketten von Methoden für eine Fluent-Schnittstelle ermöglicht wird. Die Methode build() schließt den Vorgang ab und gibt das erstellte Objekt zurück.
Verwende Builder, wenn du Objekte mit vielen optionalen Komponenten erstellst, wenn du gut lesbaren Konstruktionscode möchtest oder wenn derselbe Konstruktionsprozess verschiedene Darstellungen erzeugen soll.
Aufgabe
EinfachLass uns ein Computer Builder-System entwickeln, 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 übersichtliche, gut lesbare Möglichkeit schaffen, verschiedene Konfigurationen zusammenzustellen.
Du organisierst deinen Code über drei Dateien:
Computer.h: Definiere die Produktklasse, die einen vollständig konfigurierten Computer darstellt.Deine Klasse
Computersollte diese Komponenten als private Member speichern:cpu(String),ram(String),storage(String),gpu(String) undhasWifi(Boolean). InitialisierehasWifistandardmäßig mitfalseund die Strings mit leeren Werten.Füge eine öffentliche Methode namens
showSpecs()hinzu, die die Konfiguration des Computers anzeigt. Für jede gesetzte Komponente (nicht leerer String) soll sie eine eigene Zeile ausgeben. Die WLAN-Fähigkeit soll nur ausgegeben werden, wenn sie aktiviert ist.ComputerBuilder.h: Erstelle die Builder-Klasse, die Computer mithilfe einer Fluent-Schnittstelle erstellt.Deine Klasse
ComputerBuildersollte ein privatesComputer-Member besitzen, das sie schrittweise aufbaut. Implementiere diese Methoden, die jeweils eine Referenz auf den Builder zurückgeben, um Methodenverkettung 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-Fähigkeit
Implementiere außerdem eine
build()-Methode, die das fertigeComputer-Objekt zurückgibt und den Builder für eine mögliche Wiederverwendung zurücksetzt.main.cpp: Führe alles zusammen, um einen benutzerdefinierten Computer zu erstellen.Lies vier Eingaben ein:
- CPU-Modell (String)
- RAM-Spezifikation (String)
- Speicherspezifikation (String)
- Ob WLAN eingeschlossen werden soll:
yesoderno
Verwende den
ComputerBuilder, um mit der angegebenen CPU, dem angegebenen RAM und dem angegebenen Speicher einen Computer zu erstellen. Wenn die WLAN-Eingabeyeslautet, rufe zusätzlichaddWifi()auf. Beachte, dass in dieser Konfiguration keine GPU angegeben ist: Der Builder sollte mit optionalen Komponenten problemlos umgehen können.Rufe nach dem Erstellen auf dem resultierenden Computer
showSpecs()auf, um seine Konfiguration anzuzeigen.
Die Methode showSpecs() sollte in diesem Format ausgeben (nur gesetzte Komponenten werden angezeigt):
CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: EnabledBeispielsweise 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 SSDBeachte, wie das Builder-Muster den Erstellungsprozess übersichtlich und flexibel macht. Du kannst Komponenten einfach hinzufügen oder überspringen, und die Methodenverkettung erzeugt klaren, selbstdokumentierenden Code. Die GPU-Zeile erscheint nicht, weil wir sie nie gesetzt haben. Dadurch wird veranschaulicht, wie Builder auf natürliche Weise mit optionalen Komponenten umgehen.
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: Erstelle eine ComputerBuilder-Instanz
// TODO: Verwende Method Chaining, um CPU, RAM und Storage zu setzen
// TODO: Wenn wifiChoice "yes" ist, rufe auch addWifi() auf
// TODO: Rufe build() auf, um das Computer-Objekt zu erhalten
// TODO: Rufe showSpecs() auf dem erstellten Computer auf, 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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Worthäufigkeit13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory und Abstract FactoryBuilder-MusterBeobachter-MusterStrategie-Muster2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler