A simple example of a fluent interface
Roiy recently released a really nice c# image enhancement filters library on CodeProject. It includes a nice collection of easy to use image transformation filters - ResizeFilter, RotateFilter, ImageWatermarkFilter, etc. They follow a simple pattern:
Image myImg = Bitmap.FromFile("cat.jpg");
Image transformedImage;
ZRLabs.Yael.BasicFilters.TextWatermarkFilter watermark = new TextWatermarkFilter();
watermark.Caption = "Test";
watermark.AutomaticTextSize = true;
transformedImage = watermark.ExecuteFilter(myImg);
transformedImage.Save("cat_watermark.png", System.Drawing.Imaging.ImageFormat.Png);
I recommended he implement a fluent interface to allow chaining filter operations together, like this:
static void Main(string[] args)
{
ZRLabs.Yael.Pipeline pipeline = new ZRLabs.Yael.Pipeline("cat.jpg");
pipeline.Rotate(90)
.Watermark("Monkey")
.RoundCorners(100, Color.Bisque)
.Save("test.png");
}
That's easier than it might sound:
- I created a Pipeline class which holds a System.Drawing.Image object to maintiain the current image state
- I added a bunch of methods which accept parameters for filter properties a user would be likely to want to change (I could have added overrides to allow setting all filter properties if desired).
- Each method returns "this", meaning it returns a reference to itself. That's the magic that allows for the chained calls.
Here's a snip of the code:
public class Pipeline
{
private Image image;
public Image CurrentImage
{
get { return image; }
set { image = value; }
}
public Pipeline(string inputFilename)
{
image = Bitmap.FromFile(inputFilename);
}
public Pipeline Rotate(float Degrees)
{
RotateFilter filter = new RotateFilter();
filter.RotateDegrees = Degrees;
image = filter.ExecuteFilter(image);
return this;
}
public Pipeline BlackAndWhite()
{
BlackAndWhiteFilter filter = new BlackAndWhiteFilter();
image = filter.ExecuteFilter(image);
return this;
}
public Pipeline Watermark(string caption)
{
TextWatermarkFilter filter = new TextWatermarkFilter();
filter.Caption = caption;
filter.AutomaticTextSize = true;
image = filter.ExecuteFilter(image);
return this;
}
/* more filters here */
public void Save(string filename)
{
image.Save(filename);
}
}
Since each method returns a Pipeline object and the Pipeline exposes all the basic methods we'll need, we're all set to just keep calling methods until we're done.
What else could you do with a fluent interface?
Joshua Flanagan wrote a very nice regular expression wrapper which allows you to define a regex using a readable syntax, exposed via a very elegent fluent interface:
Pattern findGamesPattern = Pattern.With.Literal(@"<div")
.WhiteSpace.Repeat.ZeroOrMore
.Literal(@"class=""game""").WhiteSpace.Repeat.ZeroOrMore.Literal(@"id=""")
.NamedGroup("gameId", Pattern.With.Digit.Repeat.OneOrMore)
.Literal(@"-game""")
.NamedGroup("content", Pattern.With.Anything.Repeat.Lazy.ZeroOrMore)
.Literal(@"<!--gameStatus")
.WhiteSpace.Repeat.ZeroOrMore.Literal("=").WhiteSpace.Repeat.ZeroOrMore
.NamedGroup("gameState", Pattern.With.Digit.Repeat.OneOrMore)
.Literal("-->");
As Joshua notes, Ayende's Rhino Mocks uses a fluent interface as well. Milan Negovan applied this approach to his Fluent Control Container, which simplifies the task of creating properly instantiating ASP.NET controls.
When would you apply a fluent interface? I think it makes sense when the routine use of your API requires multiple sequential method calls or property settings. I think a fluent interface makes a lot of sense on top of a richer API; simple use cases can use the fluent interface and stay simple, while complex use cases can call into the base API.
The whole definition of a fluent interface is a little vague, but as Martin Fowler says, "The more the use of the API has that language like flow, the more fluent it is."