具有结构的构建器模式
Builder pattern with struct
我正在尝试用 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_)
。我做得对还是有更好的方法,例如使用结构构造函数?
I'm a little worried about unitilized uint32
你在使用它之前先初始化它,所以这个例子没有问题。然而,构建器的用户很容易忘记调用其中一个设置器,从而留下不确定的值,从而导致未定义的行为。你给它一个默认的成员初始化器来避免这种情况。
and std::move(person_) inside a Build method.
没有必要在这里重复 class 的名称。这也行得通:
return std::move(person_);
调用者了解 Build
每次初始化只能调用一次也很重要。如果构建器仅像示例中那样用作临时对象,则可以将右值引用限定符添加到 Build
函数以防止某些意外误用。
or there is a better way,
我看不到生成器的好处。为什么不直接使用聚合初始化?:
Person person {
.name = "John",
.age = 23,
.pet_names = {"rax", "rbx", "rcx"},
};
你做对了。
也可以把set函数变成模板函数,这样可以避免在为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();
}
我正在尝试用 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_)
。我做得对还是有更好的方法,例如使用结构构造函数?
I'm a little worried about unitilized uint32
你在使用它之前先初始化它,所以这个例子没有问题。然而,构建器的用户很容易忘记调用其中一个设置器,从而留下不确定的值,从而导致未定义的行为。你给它一个默认的成员初始化器来避免这种情况。
and std::move(person_) inside a Build method.
没有必要在这里重复 class 的名称。这也行得通:
return std::move(person_);
调用者了解 Build
每次初始化只能调用一次也很重要。如果构建器仅像示例中那样用作临时对象,则可以将右值引用限定符添加到 Build
函数以防止某些意外误用。
or there is a better way,
我看不到生成器的好处。为什么不直接使用聚合初始化?:
Person person {
.name = "John",
.age = 23,
.pet_names = {"rax", "rbx", "rcx"},
};
你做对了。
也可以把set函数变成模板函数,这样可以避免在为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();
}