views:

802

answers:

5

I was recently watching a webcast about how to create a fluent DSL and I have to admit, I don't understand the reasons why one would use such an approach (at least for the given example).

The webcast presented an image resizing class, that allows you to specify an input-image, resize it and save it to an output-file using the following syntax (using C#):

Sizer sizer = new Sizer();
sizer.FromImage(inputImage)
     .ToLocation(outputImage)
     .ReduceByPercent(50)
     .OutputImageFormat(ImageFormat.Jpeg)
     .Save();

I don't understand how this is better than a "conventional" method that takes some parameters:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);

From a usability point of view, this seems a lot easier to use, since it clearly tells you what the method expects as input. In contrast, with the fluent interface, nothing stops you from omitting/forgetting a parameter/method-call, for example:

sizer.ToLocation(outputImage).Save();

So on to my questions:

1 - Is there some way to improve the usability of a fluent interface (i.e. tell the user what he is expected to do)?

2 - Is this fluent interface approach just a replacement for the non existing named method parameters in C#? Would named parameters make fluent interfaces obsolete, e.g. something similar objective-C offers:

sizer.Resize(from:input, to:output, resizeBy:0.5, ..)

3 - Are fluent interfaces over-used simply because they are currently popular?

4 - Or was it just a bad example that was chosen for the webcast? In that case, tell me what the advantages of such an approach are, where does it make sense to use it.

BTW: I know about jquery, and see how easy it makes things, so I'm not looking for comments about that or other existing examples.

I'm more looking for some (general) comments to help me understand (for example) when to implement a fluent interface (instead of a classical class-library), and what to watch out for when implementing one.

+1  A: 

It's one way to implement things.

For objects that do nothing but manipulate the same item over and over again, there's nothing really wrong with it. Consider C++ Streams: they're the ultimate in this interface. Every operation returns the stream again, so you can chain together another stream operation.

If you're doing LINQ, and doing manipulation of an object over and over, this makes some sense.

However, in your design, you have to be careful. What should the behavior be if you want to deviate halfway through? (IE,

var obj1 = object.Shrink(0.50); // obj1 is now 50% of obj2
var obj2 = object.Shrink(0.75); // is ojb2 now 75% of ojb1 or is it 75% of the original?

If obj2 was 75% of the original object, then that means you're making a full copy of the object every time (and has its advantages in many cases, like if you're trying to make two instances of the same thing, but slightly differently).

If the methods simply manipulate the original object, then this kind of syntax is somewhat disingenuous. Those are manipulations on the object instead of manipulations to create a changed object.

Not all classes work like this, nor does it make sense to do this kind of design. For example, this style of design would have little to no usefulness in the design of a hardware driver or the core of a GUI application. As long as the design involves nothing but manipulating some data, this pattern isn't a bad one.

Robert P
+2  A: 

Consider:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg);

What if you used less clear variable names:

sizer.ResizeImage(i, o, x, ImageFormat.Jpeg);

Imagine you've printed this code out. It's harder to infer what these arguments are, as you don't have access to the method signature.

With the fluent interface, this is clearer:

 sizer.FromImage(i)
 .ToLocation(o)
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .Save();

Also, the order of methods is not important. This is equivalent:

 sizer.FromImage(i)
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .ToLocation(o)
 .Save();

In addition, perhaps you might have defaults for the output image format, and the reduction, so this could become:

 sizer.FromImage(i)
 .ToLocation(o)
 .Save();

This would require overloaded constructors to achieve the same effect.

toolkit
So one advantage of a fluent interface is that it ensures readable code (while a conventional interface might be more about the usability of the interface/api).
M4N
+6  A: 

2 - Is this fluent interface approach just a replacement for the non existing named method parameters in C#? Would named parameters make fluent interfaces obsolete, e.g. something similar objective-C offers:

Well yes and no. The fluid interface gives you a larger amount of flexibility. Something that could not be achieved with named params is:

sizer.FromImage(i)
 .ReduceByPercent(x)
 .Pixalize()
 .ReduceByPercent(x)
 .OutputImageFormat(ImageFormat.Jpeg)
 .ToLocation(o)
 .Save();

The FromImage, ToLocation and OutputImageFormat in the fluid interface, smell a bit to me. Instead I would have done something along these lines, which I think is much clearer.

 new Sizer("bob.jpeg") 
 .ReduceByPercent(x)
 .Pixalize()
 .ReduceByPercent(x)
 .Save("file.jpeg",ImageFormat.Jpeg);

Fluid interfaces have the same problems many programming techniques have, they can be misused, overused or underused. I think that when this technique is used effectively it can create a richer and more concise programming model. Even StringBuilder supports it.

var sb = new StringBuilder(); 
sb.AppendLine("Hello")
 .AppendLine("World");
Sam Saffron
+3  A: 

I would say that fluent interfaces are slightly overdone and I would think that you have picked just one such example.

I find fluent interfaces particularly strong when you are constructing a complex model with it. With model I mean e.g. a complex relationship of instantiated objects. The fluent interface is then a way to guide the developer to correctly construct instances of the semantic model. Such a fluent interface is then an excellent way to separate the mechanics and relationships of a model from the "grammar" that you use to construct the model, essentially shielding details from the end user and reducing the available verbs to maybe just those relevant in a particular scenario.

Your example seems a bit like overkill.

I have lately done some fluent interface on top of the SplitterContainer from Windows Forms. Arguably, the semantic model of a hierarchy of controls is somewhat complex to correctly construct. By providing a small fluent API a developer can now declaratively express how his SplitterContainer should work. Usage goes like

var s = new SplitBoxSetup();
s.AddVerticalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo()
 .AddHorizontalSplit()
 .PanelOne().PlaceControl(()=> new Label())
 .PanelTwo().PlaceControl(()=> new Panel());
form.Controls.Add(s.TopControl);

I have now reduced the complex mechanics of the control hierarchy to a couple of verbs that are relevant for the issue at hand.

Hope this helps

flq
+1  A: 

You should read Domain Driven Design by Eric Evans to get some idea why is DSL considered good design choice.

Book is full of good examples, best practice advices and design patterns. Highly recommended.

zendar