Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the point of DSLs / fluent interfaces

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.

like image 294
M4N Avatar asked Feb 25 '09 21:02

M4N


People also ask

Is fluent interface good?

Fluent interface, first coined as a term by Martin Fowler, is a very convenient way of communicating with objects in OOP. It makes their facades easier to use and understand. However, it ruins their internal design, making them more difficult to maintain.

What is the fluent user interface?

Fluent UI React is Microsoft's official front-end React-based open-source UI framework designed to build experiences that seamlessly fit into various Microsoft products. It enables highly customizable, accessible, robust, and up-to-date components using CSS in JS.

What is a fluent interface Java?

A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language.

What is a fluent interface Python?

Fluent interfaces help the user of your API to work with an object and it's methods in a more concise manner and can therefore make your API simpler and more desirable to use. Assume a basic Task object, which might look something like this: class Task(object): def name(self, name): self.


4 Answers

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 fluent 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);

Fluent 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"); 
like image 117
Sam Saffron Avatar answered Oct 08 '22 03:10

Sam Saffron


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

like image 31
flq Avatar answered Oct 08 '22 02:10

flq


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.

like image 7
toolkit Avatar answered Oct 08 '22 02:10

toolkit


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.

like image 2
Robert P Avatar answered Oct 08 '22 03:10

Robert P