Fun with Fluent Interfaces
I’ve been working on an application recently that sleeps until a specific time then checks for the existence of a particular file. While writing the application it dawned on me that I’ve written very similar code before for other clients. I hate to solve the same problem twice so I decided to create a simple API that allows me to create a new thread, have it sleep for or until a specific time, then execute a block of code. I decided that my API would also implement a fluent interface.
Fluent interfaces allow developers to work with objects in a more natural and readable fashion by chaining methods together. By building a fluent interface I can make my API not only more concise but much easier to use. Take a look at the following code:
Wait.Until("1:45 PM").Then(() => DoSomethingImportant());
By chaining methods together we’re forming a sentence describing exactly what we want to happen. We want to wait until 1:45 PM and then do something important. But how do we implement this interface? Wait is actually a static class that defines the For and Until methods. Let’s take a closer look to see what’s going on under the covers:
public static class Wait
{
public static WaitContext For(TimeSpan waitFor_)
{
if (waitFor_ <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException("waitFor", "The supplied time span is negative");
return new WaitContext(waitFor_);
}
public static WaitContext For(string waitFor_)
{
TimeSpan waitFor = default(TimeSpan);
if (!(TimeSpan.TryParse(waitFor, out waitFor)))
throw new ArgumentOutOfRangeException("waitFor", "The supplied time span is invalid.");
return For(waitFor);
}
public static WaitContext Until(DateTime waitUntil_)
{
if (waitUntil_ <= DateTime.Now)
throw new ArgumentOutOfRangeException("waitUntil_", "The supplied date is in the past.");
return new WaitContext(waitUntil_);
}
public static WaitContext Until(string waitUntil_)
{
DateTime waitUntil = default(DateTime);
if (!(DateTime.TryParse(waitUntil_, out waitUntil)))
throw new ArgumentOutOfRangeException("waitUntil_", "The supplied date is invalid.");
return Until(waitUntil);
}
}
Let’s take a second to examine this class. As you can see, we are defining overloaded versions of the Wait and For methods allowing us to supply a specific DateTime or TimeSpan or a string that can be parsed into the appropriate structure. By overloading the two key methods to allow strings we can make our interface more natural and easier to read. Each method first performs some validation logic then returns a WaitContext that defines how long we are going to wait before executing a given block of code. Let’s take a look at WaitContext so we can complete the full picture of how this is all going to work:
public class WaitContext
{
internal WaitContext(DateTime waitUntil_)
{
_waitUntil = waitUntil_;
}
internal WaitContext(TimeSpan waitFor_)
{
_waitFor = waitFor_;
}
private DateTime? _waitUntil;
private TimeSpan? _waitFor;
public void Then(Action toDo_)
{
if (toDo_ == null)
throw new ArgumentNullException("toDo_");
ThreadPool.QueueUserWorkItem(new WaitCallback(
new Action<object>(o =>
{
Thread.Sleep(
_waitUntil.HasValue ?
_waitUntil.Value.Subtract(DateTime.Now) : _waitFor.Value);
toDo_();
})));
}
}
The WaitContext can only be initialized within the Wait class defined earlier with a DateTime or TimeSpan structure. The Then method is the one that actually does all of the heavy lifting. After checking that the provided Action delegate is valid we then spin up a new thread. Within the thread we’re going to wait for or until the specified time then invoke the provided delegate. Since we’re potentially working with multiple threads here be sure to make the delegate that you provide thread-safe. If you’d like, it would be pretty simple to include some sort of callback mechanism within the delegate that you provide.
That’s pretty much all there is to it. Keep in mind that there is no one tool for every situation. Fluent interfaces just seemed to be a particularly good fit this time around. I’d like to add some additional functionality in the near future that would allow me to continuously invoke the delegate at a given interval. I don’t need that right now but I’ll probably add it later.
This source code is free to modify and use for whatever purpose. All that I ask is that you give a little credit where credit is due and let me know how you’re using it.
Happy hacking!
About this entry
You're currently reading “Fun with Fluent Interfaces,” an entry on casey.in.point
- Published:
- 10.14.09 / 12pm
- Category:
- Uncategorized



2 Comments
Jump to comment form | comments rss [?] | trackback uri [?]