是否有 C++ 中首选的构建器模式的替代方案?
Is there an alternative to the builder pattern that is preferred in C++?
我来自 Java,那里大量使用构建器模式,例如
Foo foo = new FooBuilder()
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
例如,Automapper 是一个流行的库,它通过 Java 注释生成此模式。
我没有看到任何这样的 C++ 库——只有在线的要点和博客文章以及示例临时实现。
缺少库是否意味着构建器模式在 C++ 中不是首选?那么替代或首选成语是什么?
也许它有助于描述我真正想要的东西。我喜欢构建器模式为我提供的语法,例如,如果我可以设置 20 个字段(例如 一个大型配置),但可能只设置 4 个或可能设置全部 20 个,而无需必须为每种情况创建显式构造函数。
一个常见的模式是聚合初始化:
Foo foo = {
.bar=43,
.baz="hello, world!",
.enableCache=true,
};
请注意,此处使用的指定初始化程序是在 C++20 中引入的。在此之前,您只能按位置初始化子对象。
另一种在没有指定初始化器的情况下很常见的模式是值初始化,然后是数据成员赋值:
Foo foo = {};
foo.bar = 43;
foo.baz = "hello, world!";
foo.enableCache = true;
两种模式的使用都不需要使用库。
您只需修正类型错误:
Foo foo = (*new FooBuilder())
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
剩下的就是将您的函数正确定义为始终 return FooBuilder&(构建 returns a Foo 除外)。
假设 FooBuilder 是从各种来源积累选项的东西,而不仅仅是分配成员变量。如果您的代码真的很像您创建构建器的示例,请分配所有选项并构建 Foo 然后只需使用构造函数或聚合初始化:
Foo foo = {
.bar=43,
.baz="hello, world!",
.enableCache=true,
};
更新:这是我在保留 new
时想到的用例:
FooBuilder *builer = nullptr;
if (use_bla_builder) {
builder = new Bla::FooBuilder();
} else {
builder = new Blub::FooBuilder();
}
Foo foo = (*builder)
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
delete builder;
注意:构建器是抽象的,而它构建的 Foo 是具体的。
如果这是您的代码,您可以通过设置来近似它 return *如下所示:
class Foo {
public:
Foo & setter1(int value) { ...; return *this; }
Foo & setter2(int value) { ...; return *this; }
};
Foo foo;
foo.setter1(1)
.setter2(2);
或者指针或者智能指针,或者其他什么。我开始这样做是因为它使代码更简洁。显然,如果您的构造函数做了很多,那么您仍然在做很多。如果 Foo 是一个 subclass,并且你的一些 setter 调用在基础 class 上,你也会遇到问题,因为突然间你没有 Foo return ]ed,而是一个 BaseFoo。
所以它并不完美。
我见过 self-implemented 构建器模式,它们实际上是一个合适的构建器,但坦率地说,我认为这是零收益的大量开销。
我来自 Java,那里大量使用构建器模式,例如
Foo foo = new FooBuilder()
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
例如,Automapper 是一个流行的库,它通过 Java 注释生成此模式。
我没有看到任何这样的 C++ 库——只有在线的要点和博客文章以及示例临时实现。
缺少库是否意味着构建器模式在 C++ 中不是首选?那么替代或首选成语是什么?
也许它有助于描述我真正想要的东西。我喜欢构建器模式为我提供的语法,例如,如果我可以设置 20 个字段(例如 一个大型配置),但可能只设置 4 个或可能设置全部 20 个,而无需必须为每种情况创建显式构造函数。
一个常见的模式是聚合初始化:
Foo foo = {
.bar=43,
.baz="hello, world!",
.enableCache=true,
};
请注意,此处使用的指定初始化程序是在 C++20 中引入的。在此之前,您只能按位置初始化子对象。
另一种在没有指定初始化器的情况下很常见的模式是值初始化,然后是数据成员赋值:
Foo foo = {};
foo.bar = 43;
foo.baz = "hello, world!";
foo.enableCache = true;
两种模式的使用都不需要使用库。
您只需修正类型错误:
Foo foo = (*new FooBuilder())
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
剩下的就是将您的函数正确定义为始终 return FooBuilder&(构建 returns a Foo 除外)。
假设 FooBuilder 是从各种来源积累选项的东西,而不仅仅是分配成员变量。如果您的代码真的很像您创建构建器的示例,请分配所有选项并构建 Foo 然后只需使用构造函数或聚合初始化:
Foo foo = {
.bar=43,
.baz="hello, world!",
.enableCache=true,
};
更新:这是我在保留 new
时想到的用例:
FooBuilder *builer = nullptr;
if (use_bla_builder) {
builder = new Bla::FooBuilder();
} else {
builder = new Blub::FooBuilder();
}
Foo foo = (*builder)
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
delete builder;
注意:构建器是抽象的,而它构建的 Foo 是具体的。
如果这是您的代码,您可以通过设置来近似它 return *如下所示:
class Foo {
public:
Foo & setter1(int value) { ...; return *this; }
Foo & setter2(int value) { ...; return *this; }
};
Foo foo;
foo.setter1(1)
.setter2(2);
或者指针或者智能指针,或者其他什么。我开始这样做是因为它使代码更简洁。显然,如果您的构造函数做了很多,那么您仍然在做很多。如果 Foo 是一个 subclass,并且你的一些 setter 调用在基础 class 上,你也会遇到问题,因为突然间你没有 Foo return ]ed,而是一个 BaseFoo。
所以它并不完美。
我见过 self-implemented 构建器模式,它们实际上是一个合适的构建器,但坦率地说,我认为这是零收益的大量开销。