Back to TILs

C++ Design Pattern Creational builder 01

Date: 2021-09-12Last modified: 2023-03-22

Table of contents

// ----------------------------------------------------------------------
// Person.h
// ----------------------------------------------------------------------
class PersonBuilder;

class Person {
  // address
  std::string street_address, post_code, city;

  // employment
  std::string company_name, position;
  int annual_income = 0;

  Person() { TRACE; }

 public:
  ~Person() { TRACE; }

  static PersonBuilder create();

  // clang-format off
  Person( Person &&other ) :
    street_address { std::move( other.street_address ) },
    post_code      { std::move( other.post_code ) },
    city           { std::move( other.city ) },
    company_name   { std::move( other.company_name ) },
    position       { std::move( other.position ) },
    annual_income  { other.annual_income }
  {
    TRACE;
  }
  // clang-format on

  Person &operator=(Person &&other) {
    TRACE;
    if (this == &other) return *this;
    street_address = std::move(other.street_address);
    post_code = std::move(other.post_code);
    city = std::move(other.city);
    company_name = std::move(other.company_name);
    position = std::move(other.position);
    annual_income = other.annual_income;
    return *this;
  }

  friend std::ostream &operator<<(std::ostream &os, const Person &obj) {
    TRACE;
    // clang-format off
    return os 
      << "street_address: " << obj.street_address
      << " post_code: "     << obj.post_code
      << " city: "          << obj.city
      << " company_name: "  << obj.company_name
      << " position: "      << obj.position
      << " annual_income: " << obj.annual_income;
    // clang-format on
  }

  friend class PersonBuilder;
  friend class PersonAddressBuilder;
  friend class PersonJobBuilder;
};
// ----------------------------------------------------------------------
// PersonBuilder.h
// ----------------------------------------------------------------------
class PersonAddressBuilder;
class PersonJobBuilder;

class PersonBuilderBase {
 protected:
  Person &person;
  explicit PersonBuilderBase(Person &person) : person{person} { TRACE; }

 public:
  operator Person() const {
    TRACE;
    return std::move(person);
  }

  // builder facets

  PersonAddressBuilder lives() const;
  PersonJobBuilder works() const;
};

class PersonBuilder : public PersonBuilderBase {
  Person p;

 public:
  PersonBuilder() : PersonBuilderBase{p} { TRACE; }
};
// ----------------------------------------------------------------------
// Person.cpp
// ----------------------------------------------------------------------
PersonBuilder Person::create() {
  TRACE;
  return PersonBuilder{};
}
// ----------------------------------------------------------------------
// PersonAddressBuilder.h
// ----------------------------------------------------------------------
class PersonAddressBuilder : public PersonBuilderBase {
  typedef PersonAddressBuilder Self;

 public:
  explicit PersonAddressBuilder(Person &person) : PersonBuilderBase{person} {
    TRACE;
  }

  Self &at(const std::string &street_address) {
    TRACE;
    person.street_address = street_address;
    return *this;
  }

  Self &with_postcode(const std::string &post_code) {
    TRACE;
    person.post_code = post_code;
    return *this;
  }

  Self &in(const std::string &city) {
    TRACE;
    person.city = city;
    return *this;
  }
};
// ----------------------------------------------------------------------
// PersonJobBuilder.h
// ----------------------------------------------------------------------
class PersonJobBuilder : public PersonBuilderBase {
  typedef PersonJobBuilder Self;

 public:
  explicit PersonJobBuilder(Person &person) : PersonBuilderBase{person} {
    TRACE;
  }

  Self &at(const std::string &company_name) {
    TRACE;
    person.company_name = company_name;
    return *this;
  }

  Self &as_a(const std::string &position) {
    TRACE;
    person.position = position;
    return *this;
  }

  Self &earning(int annual_income) {
    TRACE;
    person.annual_income = annual_income;
    return *this;
  }
};
// ----------------------------------------------------------------------
// PersonBuilder.cpp
// ----------------------------------------------------------------------
PersonAddressBuilder PersonBuilderBase::lives() const {
  TRACE;
  return PersonAddressBuilder{person};
}

PersonJobBuilder PersonBuilderBase::works() const {
  TRACE;
  return PersonJobBuilder{person};
}
  Person p = Person::create()
    .lives()
      .at("123 London Road")
      .with_postcode("SW1 1GB")
      .in("London")
    .works()
      .at("PragmaSoft")
      .as_a("Consultant")
      .earning(10e6);
  std::cout << "--------------" << std::endl;
  std::cout << p << std::endl;

Possible output

static PersonBuilder Person::create()
PersonBuilderBase::PersonBuilderBase(Person &)
Person::Person()
PersonBuilder::PersonBuilder()
PersonAddressBuilder PersonBuilderBase::lives() const
PersonBuilderBase::PersonBuilderBase(Person &)
PersonAddressBuilder::PersonAddressBuilder(Person &)
Self &PersonAddressBuilder::at(const std::string &)
Self &PersonAddressBuilder::with_postcode(const std::string &)
Self &PersonAddressBuilder::in(const std::string &)
PersonJobBuilder PersonBuilderBase::works() const
PersonBuilderBase::PersonBuilderBase(Person &)
PersonJobBuilder::PersonJobBuilder(Person &)
Self &PersonJobBuilder::at(const std::string &)
Self &PersonJobBuilder::as_a(const std::string &)
Self &PersonJobBuilder::earning(int)
Person PersonBuilderBase::operator Person() const
Person::Person(Person &&)
Person::~Person()
--------------
std::ostream &operator<<(std::ostream &, const Person &)
street_address: 123 London Road post_code: SW1 1GB city: London company_name: PragmaSoft position: Consultant annual_income: 10000000
Person::~Person()

References