使用 Fluent API 的 Builder 模式
Builder Pattern 将对象的创建与对象本身分离。背后的主要思想是对象不必为自己的创建负责。复杂对象的正确和有效组装本身可能是一项复杂的任务,因此可以将此任务委派给另一个类。
受 C#中的 Email Builder 的启发,我决定在这里制作一个 C++版本。Email 对象不一定是非常复杂的对象,但它可以演示模式。
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
// Forward declaring the builder
class EmailBuilder;
class Email
{
public:
friend class EmailBuilder; // the builder can access Email's privates
static EmailBuilder make();
string to_string() const {
stringstream stream;
stream << "from: " << m_from
<< "\nto: " << m_to
<< "\nsubject: " << m_subject
<< "\nbody: " << m_body;
return stream.str();
}
private:
Email() = default; // restrict construction to builder
string m_from;
string m_to;
string m_subject;
string m_body;
};
class EmailBuilder
{
public:
EmailBuilder& from(const string &from) {
m_email.m_from = from;
return *this;
}
EmailBuilder& to(const string &to) {
m_email.m_to = to;
return *this;
}
EmailBuilder& subject(const string &subject) {
m_email.m_subject = subject;
return *this;
}
EmailBuilder& body(const string &body) {
m_email.m_body = body;
return *this;
}
operator Email&&() {
return std::move(m_email); // notice the move
}
private:
Email m_email;
};
EmailBuilder Email::make()
{
return EmailBuilder();
}
// Bonus example!
std::ostream& operator <<(std::ostream& stream, const Email& email)
{
stream << email.to_string();
return stream;
}
int main()
{
Email mail = Email::make().from("me@mail.com")
.to("you@mail.com")
.subject("C++ builders")
.body("I like this API, don't you?");
cout << mail << endl;
}
对于旧版本的 C++,可以忽略 std::move
操作并从转换运算符中删除&&(尽管这将创建临时副本)。
构建器在发布 operator Email&&()
构建的电子邮件时完成其工作。在此示例中,构建器是临时对象,并在销毁之前返回电子邮件。你还可以使用像 Email EmailBuilder::build() {...}
这样的显式操作,而不是转换运算符。
通过建设者
Builder Pattern 提供的一个很棒的功能是能够**使用多个 actor 一起构建一个对象。**这是通过将构建器传递给其他 actor 来完成的,每个 actor 都会向构建的对象提供更多信息。当你构建某种查询,添加过滤器和其他规范时,这非常强大。
void add_addresses(EmailBuilder& builder)
{
builder.from("me@mail.com")
.to("you@mail.com");
}
void compose_mail(EmailBuilder& builder)
{
builder.subject("I know the subject")
.body("And the body. Someone else knows the addresses.");
}
int main()
{
EmailBuilder builder;
add_addresses(builder);
compose_mail(builder);
Email mail = builder;
cout << mail << endl;
}
设计变体:可变对象
你可以更改此模式的设计以满足你的需求。我会给一个变种。
在给定的示例中,Email 对象是不可变的,即,它的属性无法修改,因为无法访问它们。这是一个理想的功能。如果你需要在创建对象后对其进行修改,则必须为其提供一些 setter。由于这些 setter 将在构建器中重复,因此你可以考虑在一个类中完成所有操作(不再需要构建器类)。不过,我认为首先需要使构建的对象变得可变。