Fluent object construction

I like an idea to replace constructor overloads that take optional parameters with more fluent approach, so there will be less (and easier for understanding) constructor overloads and more readable code. Perhaps also more writeable.

Let’s take an example, well-known StreamWriter. It has the following constructors:

public StreamWriter(Stream stream);
public StreamWriter(string path);
public StreamWriter(Stream stream, Encoding encoding);
public StreamWriter(string path, bool append);
public StreamWriter(Stream stream, Encoding encoding, int bufferSize);
public StreamWriter(string path, bool append, Encoding encoding);
public StreamWriter(string path, bool append, Encoding encoding, int bufferSize);

Stream and path parameters are mutually exclusive and one of them must be sent to a constructor, other parameters (encoding, append and bufferSize) are optional, and append flag can only be used together with the path. There are in total 8 constructor overloads, and they don’t cover all possible valid combination: there is no overload that only takes path, encoding and bufferSize (you will have to send append flag too).

What if this class had fewer constructors but provided additional methods to enrich stream writer setup? Like this:

public class FluentStreamWriter
{
    public Encoding Encoding { get; private set; }
    public int BufferSize { get; private set; }

    public FluentStreamWriter(Stream stream) { }
    public FluentStreamWriter(string path) { }
    public FluentStreamWriter(string path, bool append) { }

    public FluentStreamWriter With(Encoding encoding, int bufferSize) { this.Encoding = encoding; this.BufferSize = bufferSize; return this; }
    public FluentStreamWriter WithEncoding(Encoding encoding) { this.Encoding = encoding; return this; }
    public FluentStreamWriter WithBufferSize(int bufferSize) { this.BufferSize = bufferSize; return this; }
}

And the code using classic and fluently constructed stream writer would look like this:

var writer1 = new StreamWriter("temp.txt", false, Encoding.UTF8);
var writer2 = new StreamWriter("temp.txt", true, Encoding.UTF8, 1000);

var writer3 = new FluentStreamWriter("temp.txt").With(Encoding.UTF8, 1000);
var writer4 = new FluentStreamWriter("temp.txt").WithEncoding(Encoding.UTF8);
var writer5 = new FluentStreamWriter("temp.txt").WithEncoding(Encoding.UTF8).WithBufferSize(1000);

What I like in this approach is the separation between mandatory and optional set of constuctor arguments: what is sent to the constructor is mandatory (either individual parameters or set of mutually exclusive parameters). Everything that is optional may be added using methods prefixed with “With” that return the same class instance.

The drawback of this approach is that since it moves some object construction options to custom methods, they can be overlooked by developers that are not aware of API style and may conclude that object construction possibilities are limited by the list of constructor overloads. So perhaps this approach is not suitable for a small API where fluent construction will be the only deviation from traditional type design style. But in large frameworks with fluent API style it can feel natural and be easily accepted.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s