How to implement the Builder Design Pattern with the use of smart pointers?

514 Views Asked by At

I want to implement the builder pattern in C++ for a project that I am working on. I have found plenty of examples of this design pattern that uses raw pointers, but none that uses smart pointers. Since my opinion is that smart pointers are the way to go when writing modern C++ code, I wonder how the builder pattern can be implemented with the use of smart pointers for the director, builder, product et cetera.

I use the conceptual example code of Refactoring.Guru for the C++ builder design pattern as reference: https://refactoring.guru/design-patterns/builder/cpp/example.

How can I transform this conceptual example into a modern C++ builder pattern with the usage of smart pointers? I understand that where raw pointers are present, you can replace them with smart pointers, but i am mostly struggling with where to use unique pointers and shared pointers and how to pass them from function to function. Do I use unique pointers and just move them in every function call or do I use shared pointers in the case of the product for example?

It's a shame Refactoring.Guru does not include such a version of this example.

Thanks in advance.

1

There are 1 best solutions below

0
gera verbun On

Here is an example of a regular Builder design pattern that I based on Builder Design Pattern

Here is an implementation to smart pointers:

#include <iostream>
#include <memory>
#include <string>

class Product {
public:
    void set_part_a(std::string const& part_a) {
        part_a_ = part_a;
    }

    void set_part_b(std::string const& part_b) {
        part_b_ = part_b;
    }

    void set_part_c(std::string const& part_c) {
        part_c_ = part_c;
    }

    void show() const {
        std::cout << "Part A: " << part_a_ << "\n";
        std::cout << "Part B: " << part_b_ << "\n";
        std::cout << "Part C: " << part_c_ << "\n";
    }

private:
    std::string part_a_;
    std::string part_b_;
    std::string part_c_;
};

class Builder {
public:
    virtual ~Builder() = default;

    virtual void build_part_a() = 0;
    virtual void build_part_b() = 0;
    virtual void build_part_c() = 0;
    virtual std::unique_ptr<Product> get_product() = 0;
};

class ConcreteBuilder: public Builder {
public:
    ConcreteBuilder(): product_{std::make_unique<Product>()} {}

    void build_part_a() override {
        product_->set_part_a("Part A");
    }

    void build_part_b() override {
        product_->set_part_b("Part B");
    }

    void build_part_c() override {
        product_->set_part_c("Part C");
    }

    std::unique_ptr<Product> get_product() override {
        return std::move(product_);
    }

private:
    std::unique_ptr<Product> product_;
};

class Director {
public:
    void set_builder(std::shared_ptr<Builder> const& builder) {
        builder_ = builder;
    }

    void construct() {
        builder_->build_part_a();
        builder_->build_part_b();
        builder_->build_part_c();
    }

private:
    std::shared_ptr<Builder> builder_;
};

int main() {
    std::shared_ptr<ConcreteBuilder> builder = std::make_shared<ConcreteBuilder>();
    std::shared_ptr<Director> director = std::make_shared<Director>();

    director->set_builder(builder);
   director->construct();

    std::unique_ptr<Product> product = builder->get_product();
    product->show();

    return 0;
}

Output:

Part A: Part A
Part B: Part B
Part C: Part C

And if you want more generic here is a very generic example:

#include <iostream>
#include <memory>
#include <string>

template <typename PartA, typename PartB, typename PartC>
class Product {
public:
    void set_part_a(PartA const& part_a) {
        part_a_ = part_a;
    }

    void set_part_b(PartB const& part_b) {
        part_b_ = part_b;
    }

    void set_part_c(PartC const& part_c) {
        part_c_ = part_c;
    }

    void show() const {
        std::cout << "Part A: " << part_a_ << "\n";
        std::cout << "Part B: " << part_b_ << "\n";
        std::cout << "Part C: " << part_c_ << "\n";
    }

private:
    PartA part_a_;
    PartB part_b_;
    PartC part_c_;
};

template <typename PartA, typename PartB, typename PartC>
class Builder {
public:
    virtual ~Builder() = default;

    virtual void build_part_a(PartA const&) = 0;
    virtual void build_part_b(PartB const&) = 0;
    virtual void build_part_c(PartC const&) = 0;
    virtual std::unique_ptr<Product<PartA, PartB, PartC>> get_product() = 0;
};

template <typename PartA, typename PartB, typename PartC>
class ConcreteBuilder: public Builder<PartA, PartB, PartC> {
public:
    void build_part_a(PartA const& part_a) override {
        product_.set_part_a(part_a);
    }

    void build_part_b(PartB const& part_b) override {
        product_.set_part_b(part_b);
    }

    void build_part_c(PartC const& part_c) override {
        product_.set_part_c(part_c);
    }

    std::unique_ptr<Product<PartA, PartB, PartC>> get_product() override {
        return std::make_unique<Product<PartA, PartB, PartC>>(product_);
    }

private:
    Product<PartA, PartB, PartC> product_;
};

template <typename PartA, typename PartB, typename PartC>
class Director {
public:
    void set_builder(std::shared_ptr<Builder<PartA, PartB, PartC>> const& builder) {
        builder_ = builder;
    }

    void construct(PartA const& part_a, PartB const& part_b, PartC const& part_c) {
        builder_->build_part_a(part_a);
        builder_->build_part_b(part_b);
        builder_->build_part_c(part_c);
    }

private:
    std::shared_ptr<Builder<PartA, PartB, PartC>> builder_;
};

int main() {
    std::shared_ptr<ConcreteBuilder<std::string, int, double>> builder = std::make_shared<ConcreteBuilder<std::string, int, double>>();
    std::shared_ptr<Director<std::string, int, double>> director = std::make_shared<Director<std::string, int, double>>();

    director->set_builder(builder);
    director->construct("AAA", 42, 3.14);

    std::unique_ptr<Product<std::string, int, double>> product = builder->get_product();
    product->show();

    return 0;
}

Output:

Part A: AAA
Part B: 42
Part C: 3.14