I'm coming from Java where the builder pattern is used heavily, e.g.
Foo foo = new FooBuilder()
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
Automapper for example is popular library that generates this pattern via Java annotations.
I don't see any such library for C —only gists and blog posts online with example ad hoc implementations.
Does the lack of a library imply that the builder pattern is not preferred in C ? What is the alternative or preferred idiom then?
Maybe it helps to describe what I actually want. I like the grammar that the builder pattern affords me, for example if there are 20 fields I could set (e.g. a large configuration), but may only set 4 or may set all 20, without having to create explicit constructors for each case.
CodePudding user response:
A common pattern is aggregate initialisation:
Foo foo = {
.bar=43,
.baz="hello, world!",
.enableCache=true,
};
Note that designated initialisers such as used here were introduced in C 20. Prior to that, you could only initialise sub objects positionally.
Another pattern, common in absence of designated initialisers, is value initialisation followed by assignment of data members:
Foo foo = {};
foo.bar = 43;
foo.baz = "hello, world!";
foo.enableCache = true;
Usage of neither pattern requires the use of a library.
CodePudding user response:
If it's your code, you can approximate it by having your settings return *this as follows:
class Foo {
public:
Foo & setter1(int value) { ...; return *this; }
Foo & setter2(int value) { ...; return *this; }
};
Foo foo;
foo.setter1(1)
.setter2(2);
Or pointers or smart pointers, or whatever. I've taken to doing this because it makes for slightly cleaner code. Clearly, if your constructor does a lot, you're still doing a lot. You also have a problem if Foo is a subclass, and some of your setter calls are on the base class, because suddenly you don't have a Foo being returned, but a BaseFoo.
So it's not perfect.
I've seen self-implemented builder patterns that actually are a proper builder, but frankly, I think that's a lot of overhead with zero gain.
CodePudding user response:
You just have to fix the type error:
Foo foo = (*new FooBuilder())
.setBar(43)
.setBaz("hello, world!")
.enableCache(true)
.build();
The rest is defining your functions propertly to always return a FooBuilder& (except build returns a Foo).
That's assuming FooBuilder is something that accumulates options from various sources and doesn't just assign member variables. If your code is really like your example where you create the builder, assign all the options and build the Foo then just use a constructor or aggregate initialization:
Foo foo = {
.bar=43,
.baz="hello, world!",
.enableCache=true,
};
Update: Here is the use case I had in mind when retaining the 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;
Note: The builder is abstract while the Foo it builds is concrete.