Fluent Interfaces
Oren recently posted an article comparing a Domain Specific Language (DSL) and a Fluent Interface.
I'm still not entirely sure I understand the differences between a DSL and a fluent interface, but I didn't think that Oren really pushed the fluent interface example quite as far as he could have, so I thought I'd play with the idea and see what I could come up with.
For comparison, here's Oren's example:
new FluentTask("warn if website is not alive")
.Every( TimeSpan.FromMinutes(3) )
.StartingFrom( DateTime.Now )
.When(delegate
{
return WebSite("http://example.org")
.IsAlive == false;
})
.Execute(delegate
{
Notify(“admin@example.org”, “server down!”);
});
Here's what I came up with in about 30-45 minutes:
ITask SiteCheckTask =
Schedule.Task( "Report website down-time" ).
Repeat.Every( 3 ).Minutes.
Starting( DateTime.Now ).
If( Web.Site( "http://www.example.com" )
.IsNotResponding() ).
Notify( "admin@example.com", "Site down!" );
Although it reads quite nicely from the user's perspective, behind the scenes there are eight classes and six interfaces, which makes this small demo quite complex.
This works because most of the methods and properties are factory methods. For example, Schedule.Task( "Report website down-time" ) is a static method which returns an instance of a TaskDescriptor class:
public class Schedule
{
public static ITaskDescriptor Task( string description )
{
return new TaskDescriptor( description );
}
}
The Repeat property of the TaskDescriptor then does the same again: it creates an instance of a ScheduleDescriptor (passing itself in) and returns that. The ScheduleDescriptor describes the start and interval for the scheduled task.
public IScheduleDescriptor Repeat
{
get { return new ScheduleDescriptor( this ); }
}
And so on. The ScheduleDescriptor's Every() method sets an internal interval field and returns itself to the caller ( return this; ). The Starting method of the ScheduleDescriptor records the DateTime, and then sets itself as the ScheduleDetails on the TaskDescriptor, which it then returns.
public ITaskDescriptor Starting( DateTime start )
{
startTime = start;
descriptor.ScheduleDetails = this;
return descriptor;
}
The If() method on the TaskDescriptor does much the same as the Repeat property did, but it creates an ActionDescriptor this time. It accepts a StatusCheckDelegate as a parameter, which is built with the Web static class' Site() method. This returns an instance of a WebStateCheckDescriptor (beginning to see a pattern here?) which takes a string for the URL, and the IsNotResponding() method returns a delegate which (nearly) checks whether the specified URL is accessible.
Phew. Plenty of indirection there!! But the question is: does it add too much complexity? I think that is probably one of those questions which has to be answered on a per-project basis.
You can have a trawl through the code to see what I've done. It's certainly not anywhere near production (it doesn't even do anything) but hopefully it might be useful 
References
Anders Norås' "Behind the scenes of the planning DSL" was the inspiration for this spike.
Feedback
While Resharper made this almost trivial to hack together, if I'd tried to do this test-first, I think I'd still be writing it now.
The fact that it bends the way you use OOP (properties as factory methods, for example) also increases the complexity and I think many would consider it a "smell" under other circumstances.
Its certainly clever but for me whilst the code in Main is nice to read the hoops you've had to jump through to get there are way too much for me.
I definitely get syntactic sugar but when I have tried to do fluent interfaces in C# I've ended up with the sort of solution you have here.
My view is that *using* fluent interfaces is nice but I'm guessing that for most situations they are overkill because they do add to the complexity.