Immutable objects in Fluent Interfaces: a lesson from Functional Programming
Fluent interfaces are a nice way of building up an API which reads very nicely. Chaining methods together is often a big part of fluent interfaces, but doing so isn't always as straightforward as it looks, especially for configuration objects. Suppose I'm building up some database meta-data, and I'm defining a new column using method chaining:
var firstColumn = DefinedAs.TextColumn("name");
Now suppose I have another table which also happens to have a text column called 'name', except it also has a default value. To save typing I decide to build on top of the column definition I already have:
var secondColumn = firstColumn.WithDefaultValue("Jane Doe");
If the WithDefaultValue() method is implemented the wrong way it will directly modify the firstColumn structure, which is a very unfluent thing as it doesn't fit with the name of the method (a method called 'SetDefaultValue' might be expected to modify the existing object but 'WithDefaultValue' sounds like it is creating a new object). Instead, for an interface like this, I think that an existing configuration should never be modified. Instead, applying an option to a configuration object should always return a new configuration object. Doing this means that firstColumn and secondColumn will refer to different objects and the API won't surprise the user.
(Of course the functional programming people knew this all along)
The expense of creating the extra objects is something to consider, but in the vast majority of cases creating configuration objects like this shouldn't be a big part of application runtime.