I need to initialize several parameters of an object but I also wanted to make it immutable. At the same time I don't want the constructor to take all several parameters because some of them are optional and have default values that the user can change during initialization like an IReadOnlyDictionary
so I was experimenting with different patterns.
I found out that there is an interesting fact that if you make the builder nested inside the actual immutable class it then has still access to its private setters. This means you can build the actual object using the actual object without having to create copies in the builder itself and it still is immutable after exporting it, isnt't it?
This is how it looks like:
class Foo
{
private IDictionary<string, string> _corge =
new Dictionary<string, string>();
public Foo() { }
public string Bar { get; private set; }
public IReadOnlyDictionary<string, string> Corge =>
(IReadOnlyDictionary<string, string>)_corge;
// API v3
public static Foo Build(Action<Builder> build)
{
return Builder.Build(build);
}
public class Builder
{
private Foo _foo = new Foo();
private Builder() { }
// API v1
public static Builder Create()
{
return new Builder();
}
// API v2
public static Foo Build(Action<Builder> build)
{
var builder = new Builder();
build(builder);
return builder.ToFoo();
}
public Builder Bar(string bar)
{
_foo.Bar = bar;
return this;
}
public Builder AddCorge(string key, string value)
{
_foo._corge.Add(key, value);
return this;
}
public Foo ToFoo()
{
// prevents from reusing the builder after "ToFoo"
var result = _foo;
_foo = null;
return result;
}
}
}
Usage with API v1:
var fooBuilder = Foo.Builder.Create();
fooBuilder.Bar("baz");
var foo = fooBuilder.ToFoo();
fooBuilder.Bar("QUUX"); // bam!
or with API v2:
var foo2 = Foo.Builder.Build(b => b.Bar("baz"));
or even shorther with API v3:
var foo2 = Foo.Build(b => b.Bar("baz"));