Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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) und hasWifi (boolean). Initialisieren Sie hasWifi auf false und 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 privates Computer-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 fertige Computer-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:

    1. CPU-Modell (string)
    2. RAM-Spezifikation (string)
    3. Speicher-Spezifikation (string)
    4. Ob WLAN enthalten sein soll: yes oder no

    Verwenden Sie den ComputerBuilder, um einen Computer mit der angegebenen CPU, dem RAM und dem Speicher zu konstruieren. Wenn die WLAN-Eingabe yes ist, rufen Sie zusätzlich addWifi() 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: Enabled

Zum 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: Enabled

Mit den Eingaben AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD und no:

CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSD

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