There are two common ways to create a fluent interface.
One way is to add to the current instance of the class being built and return this
from each method.
Something like this:
public class NamesBuilder
{
private List<string> _names = new List<string>();
public NamesBuilder AddName(string name)
{
_names.Add(name);
return this;
}
}
The problem with this kind of builder is that you can write buggy code easily:
var namesBuilder = new NamesBuilder();
var namesBuilder1 = namesBuilder.AddName("John");
var namesBuilder2 = namesBuilder.AddName("Jack");
If I saw this code I would expect that namesBuilder1
and namesBuilder2
would each only have one name, and that namesBuilder
wouldn't have any. However the implementation would have both names in all three variables as they are the same instance.
The better way to implement a fluent interface is to create a chain on builder classes that are lazily evaluated so that you create the final class once you're done building. Then if you branch in the middle of the building process you can not make a mistake.
Here's the kind of code I would expect to write:
var bookMap =
ModelStateMappings
.Build<Book, BookViewModel>()
.AddProperty(book => book.Author, vm => vm.AuthorsName)
.AddProperty(book => book.Price, vm => vm.BookPrice)
.Create();
var bookStore =
ModelStateMappings
.Build<Store, StoreViewModel>()
.AddProperty(store => store.Owner, vm => vm.OwnersName)
.AddProperty(store => store.Location, vm => vm.Location)
.Create();
The code to make this work is a little more complicated than the "names" example.
public static class ModelStateMappings
{
public static Builder<M, VM> Build<M, VM>()
{
return new Builder<M, VM>();
}
public class Builder<M, VM>
{
public Builder() { }
public Builder<M, VM> AddProperty<T>(
Expression<Func<M, T>> domainMap,
Expression<Func<VM, T>> viewModelMap)
{
return new BuilderProperty<M, VM, T>(this, domainMap, viewModelMap);
}
public virtual Map Create()
{
return new Map();
}
}
public class BuilderProperty<M, VM, T> : Builder<M, VM>
{
private Builder<M, VM> _previousBuilder;
private Expression<Func<M, T>> _domainMap;
private Expression<Func<VM, T>> _viewModelMap;
public BuilderProperty(
Builder<M, VM> previousBuilder,
Expression<Func<M, T>> domainMap,
Expression<Func<VM, T>> viewModelMap)
{
_previousBuilder = previousBuilder;
_domainMap = domainMap;
_viewModelMap = viewModelMap;
}
public override Map Create()
{
var map = _previousBuilder.Create();
/* code to add current map to Map class */
return map;
}
}
}
The other advantage to this type of builder is that you also maintain strongly-typed property fields.
Of course you would need to put in the correct code for your mapping in the Create
method.