I blogged about fluent interfaces and expression builders a couple of times before. For this post, I want to share a base class that I’ve been using for taking away some of the burden when creating expression builders for domain classes.
Here’s the expression I’m after:
var document = DocumentBuilder.BuildDocument() .AuthoredBy("Stephen Hawking") .Titled("The Universe in a Nutshell") .TaggedWith(tag => tag.Named("Physics")) .Build();
This creates an instance of a domain class named Document with the name of the author, its title and with an associated tag. Let me first show the code of the expression builders that make this happen.
public interface IDocumentAuthorBuilder { IDocumentTitleBuilder AuthoredBy(String author); } public interface IDocumentTitleBuilder { IDocumentTagBuilder Titled(String title); } public interface IDocumentTagBuilder : IBuilder<Document> { IDocumentTagBuilder TaggedWith(Action<ITagBuilder> buildUsing); } public class DocumentBuilder : Builder<Document>, IDocumentAuthorBuilder, IDocumentTitleBuilder, IDocumentTagBuilder { private DocumentBuilder() {} public static IDocumentAuthorBuilder BuildDocument() { return new DocumentBuilder(); } IDocumentTitleBuilder AuthoredBy(String author) { ProvideValueFor(document => document.Author, author); return this; } IDocumentTagBuilder Titled(String title) { ProvideValueFor(document => document.Title, title); return this; } public IDocumentTagBuilder TaggedWith(Action<ITagBuilder> buildUsing) { var tagBuilder = new TagBuilder(tag => ProvideValueFor(document => document.Tags, tag)); buildUsing(tagBuilder); return this; } } public interface ITagBuilder { void Named(String name); } public class TagBuilder : ITagBuilder { private readonly Action<Tag> _afterBuildAction; public TagBuilder(Action<Tag> afterBuildAction) { _afterBuildAction = afterBuildAction; } public void Named(String name) { var tag = new Tag(name); _afterBuildAction(tag); } }
The expression builders all provide progressive interfaces. Also notice that the DocumentBuilder derives from a base class named Builder. This class provides a method ProvideValueFor that is used to feed the base class with the name of a property and a corresponding value. Collections are also supported. Here’s the code for the Builder class.
public interface IBuilder<T> { T Build(); } public abstract class Builder<T> : IBuilder<T> { private Dictionary<PropertyInfo, Object> PropertiesAndValues { get; set; } protected Builder() { PropertiesAndValues = new Dictionary<PropertyInfo, Object>(); } public static implicit operator T(Builder<T> builder) { return builder.Build(); } protected void ProvideValueFor(Expression<Func<T, Object>> expression, Object value) { var property = ReflectionHelper.GetProperty(expression); if(false == PropertiesAndValues.ContainsKey(property)) RegisterPropertyAndValue(property, value); else SetPropertyAndValue(property, value); } private void SetPropertyAndValue(PropertyInfo property, Object value) { if(IsCollection(property)) { var values = (List<Object>) PropertiesAndValues[property]; values.Add(value); } else { PropertiesAndValues[property] = value; } } private void RegisterPropertyAndValue(PropertyInfo property, Object value) { if(IsCollection(property)) PropertiesAndValues.Add(property, new List<Object>() { value }); else PropertiesAndValues.Add(property, value); } private static Boolean IsCollection(PropertyInfo property) { if(property.PropertyType == typeof(String)) return false; var collectionType = typeof(IEnumerable<>); return IsCollectionOfType(collectionType, property.PropertyType); } private static Boolean IsCollection(FieldInfo field) { var collectionType = typeof(ICollection<>); return IsCollectionOfType(collectionType, field.FieldType); } private static Boolean IsCollectionOfType(Type collectionType, Type type) { if(collectionType.Name == type.Name) return true; var interfaces = type.GetInterfaces(); return interfaces.Has(@interface => @interface.Name == collectionType.Name); } public T Build() { var typeToBuild = typeof(T); if(false == HasParameterlessConstructor(typeToBuild)) throw new InvalidOperationException( "No parameterless constructor."); var instance = (T)Activator.CreateInstance(typeToBuild, true); foreach(var entry in PropertiesAndValues) { var property = entry.Key; if(IsCollection(property)) SetCollectionValuesFor(property, instance, (List<Object>) entry.Value); else SetValueFor(property, instance, entry.Value); } return instance; } private static Boolean HasParameterlessConstructor(Type type) { const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var defaultConstructor = type.GetConstructor(bindingFlags, null, new Type[0], null); return null != defaultConstructor; } private static void SetValueFor(PropertyInfo property, T instance, Object value) { property.SetValue(instance, value, null); } private static void SetCollectionValuesFor(PropertyInfo property, T instance, List<Object> values) { var backingField = BackingFieldResolver.GetBackingField(property); if(false == IsCollection(backingField)) { var message = String.Format( ResourceLoader<Builder<T>> .GetString("InvalidCollectionType"), property.Name); throw new InvalidOperationException(message); } var collection = property.GetValue(instance, null); foreach(var value in values) { const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod; backingField.FieldType .InvokeMember("Add", bindingFlags, null, collection, new[] { value }); } } }
Using this approach, its no longer necessary to make any compromise of exposing property setters or a dedicated constructor just for serving the expression builders. The Builder class uses refection to set the value of a property or to fill a collection. The BackingFieldResolver is a class I picked up from this post. Very cool stuff!
I have only used this approach in a couple of side projects, but let me know you think.
Till next time.
While it may be a fun exercise in learning language features. I’ve come to absolutely loath most of these “fluent” thingamajiggers.
All that work to accomplish what exactly?
This?:
var doc = new Document();
doc.Author = “Stephen Hawking”;
doc.Title = “The Universe in a Nutshell”;
doc.AddTag(new Tag{Name = “Physics”});
@Chris: Don’t you know that setters are out of fashion? 🙂
https://elegantcode.com/2008/09/26/properties-a-false-sense-of-encapsulation/comment-page-1/
Do you consider C# 4.0’s optional parameters as a solution for this situation:
/* ctor */
public Document(
string author = “”,
string title = “”,
IList tags = null)
{
/* … */
}
/* usage */
var documnet = new Document(
author: “Stephen Hawking”,
title: “John Doe”,
tags: new[] { “Physics” });
@Huseyin: I’m not a big fan of optional parameters. Using the Builder class doesn’t require a dedicated constructor nor property setters. OO at its best.
Fluent has it’s places but it’s a heavyweight. I’d argue that Setters don’t break encapsulation; They *can* break it if you just wrap your private field with a getter & setter it’s no different than just having a public field. However, setters can define the rules around if/how private data can be modified. IMO that preserves encapsulation. Fluent constructs have their flaws and abuses as well. (I’m currently in the process of testing some of their bending/breaking points)
Personally, I currently lean towards setters, especially now that they “fixed” them:
public string myProperty
{
get {}
internal set {}
}
@Steve Changing state of an object should be done through behavior. I don’t consider a property setter to be the right place for this kind of behavior (although technically yes it compiles down to a method). With setters, we get objects that temporarily become invalid while changing the different setters. With behavior we specify a method that performs our state changes in a single atomic operation.
@Jan Van Ryswyck
But even fluent interfaces are not the way to add behavior to your objects. Under the covers, fluent interface will do mostly to same actions as properties – setting values on object, and will do it without any context.
If you want to add behavior it would sit in object itself or in specialized classes, like factories:
document.UpdateDetails(author, title, [] tags);
I’d like to second Chris Martin’s comment, but go further:
var doc = new Document
{
Author = “Stephen Hawking”,
Title = “The Universe in a Nutshell”,
Tags = new[]
{
new Tag { Name = “Physics” }
}
};
Where ‘Tags’ is an IEnumerable property.
Or, even better (if these things are required for the constructed object to make sense):
var doc = new Document(“Stephen Hawking”,
“The Universe in a Nutshell”,
new Tag { Name = “Physics” });
Where the third parameter is ‘params Tag[] tags’ to allow multiple tags.
Fluent methods are a syntax hack used in less expressive languages (Java, C ) and have no real place in C# 3.0 anymore.
Hi. Great stuff, I’ve got builders liberally sprinkled around my domain and I think they are invaluable.
Any chance you could bundle the code up into a zip? It has a couple of dependencies (ReflectionHelper, BackingFieldResolver (which itself seems to depend on Mono)) and I think you have an extension method or two in there(Has and Name on Type).
Setters are the work of the devil. There’s a view that Getters aren’t too clever either but that’s a whole nother can o worms.
@Johnny Hall
Can you give few examples how you’re using it in your domain?
Because i’m strugling to see any usefulness of fluent interfaces outside of places where a kind of configuration API is required. Here, done right, the fluency works very well because of it’s discoverable nature.
But to use it as a replacement for property setters – in my opinion this sounds wrong…
I have a (configurable, multi-tenant) navigation system, where I have to set up pages with a bunch of properties and relationships. Without the builder, I have either created (the initial configuration) pages directly from the constructor, or from a factory. Both ways, the code very quickly ends up looking a mess.
Now I have something like this:
var security = NavigationBuilder
.ForApplication(Application.Security)
.ForTenant(Root.Tenant)
.AddPages(
PageBuilder.Named(“main”),
PageBuilder.Named(“home”).AsController(),
PageBuilder.Named(“dashboard”),
PageBuilder.Named(“administration”).ForParent(“main”).AsController()
)
.Build();
The indentation inside AddPages is simply there to make it a bit more readable.
Ah, ok, the indentation has disappeared. But it’s there in spirit.
@Valeriu Caraulean
And I guess that code is a configuration API.
Is there a reflectionhelper class that goes with this?
@Valeriu: Expression builders are especially useful to create and set up an entire aggregate root with all its entities and value objects. No place for behavior. I was just making a point against setters on domain objects.
@Johnny: Agreed on getters. Very hard to do and I’ve learned to live with them. Using static analysis for preventing procedural abuse. The code is part of a small sample app I’ve been working on. Maybe at a given point I’ll put it out there somewhere.
This is the code for the ReflectionHelper class.
http://snipplr.com/view/17521/reflectionhelper/
That’s indeed a cool usage of the backing field resolver. Glad that it is useful to you!