结构体实现的建造者模式。

3

我正在尝试在C++中实现建造者模式。目前我已经得到了以下内容:

struct Person {
  std::string name;
  uint32_t age;
  std::vector<std::string> pet_names;
};


class PersonBuilder {
public:
  PersonBuilder& SetName(std::string name) {
    person_.name = std::move(name);
    return *this;
  }

  PersonBuilder& SetAge(uint32_t age) {
    person_.age = age;
    return *this;
  }

  PersonBuilder& SetPetNames(std::vector<std::string> pet_names) {
    person_.pet_names = std::move(pet_names);
    return *this;
  }

  Person Build() {
    return Person(std::move(person_));
  }

private:
  Person person_;
};

int main() {
  auto person = PersonBuilder()
      .SetName("John")
      .SetAge(23)
      .SetPetNames({"rax", "rbx", "rcx"})
      .Build();

  return EXIT_SUCCESS;
}

我有点担心未使用的 uint32,以及在 Build 方法中使用 std::move(person_)。我这样做对吗?还是有更好的方法,比如使用结构体构造函数?


1
你可以在年龄声明中使用 "uint32_t age = 0"。 - Cleiton Santoia Silva
1
Person Build() { auto result = move(person_); person_ = Person(); return result; } 另外,Cleiton说要对Person的age成员变量进行默认初始化。 - Eljay
1
如果您返回一个本地变量,它总是会移动,因此请将其放在本地,然后返回本地。另外,您没有正确重置person_field。 - Cleiton Santoia Silva
2个回答

2

我有点担心未使用的uint32

在使用之前,您需要初始化它,所以在示例中没有问题。但是,很容易忘记调用其中一个setter方法,导致该变量具有不确定的值,从而产生未定义行为。您可以给它一个默认成员初始化器来避免这种情况。

还有Build方法中的std :: move(person_)。

在这里重复类名是不必要的。以下代码也可以正常工作:

return std::move(person_);

重要提示:调用者必须明白 `Build` 只能在初始化时被调用一次。如果该构建器只用于临时的情况(如示例中),可以给 `Build` 函数添加右值引用限定符以防止意外误用。

或者有更好的方法,

我不认为使用构建器有任何好处。为什么不直接使用聚合初始化?:

Person person {
    .name = "John",
    .age = 23,
    .pet_names = {"rax", "rbx", "rcx"},
};

“为什么不直接使用聚合初始化呢?”因为指定初始化器需要遵循顺序。 - 康桓瑋
@康桓瑋 我认为这是一个好处。 - eerorika

2

你做得很对。

你也可以将设置函数转化为模板函数,这样可以避免在为Person添加新成员变量时修改PersonBuilder

struct Person {
  std::string name;
  uint32_t age;
  std::vector<std::string> pet_names;
};

class PersonBuilder {
public:
  template<auto mem_ptr>
  PersonBuilder& Set(std::remove_reference_t<std::invoke_result_t<decltype(mem_ptr), Person&>> value = {}) {
    std::invoke(mem_ptr, person_) = std::move(value);
    return *this;
  }

  Person Build() {
    auto result = std::move(person_); 
    person_ = Person(); 
    return result;
  }

private:
  Person person_;
};

int main() {
  auto person = PersonBuilder()
    .Set<&Person::name>("John")
    .Set<&Person::age>(23)
    .Set<&Person::pet_names>({"rax", "rbx", "rcx"})
    .Build();
}

演示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接