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."