Lecture
Fluent interface (in the sense of a "smooth" or "flowing" interface) in software development is a way of implementing an object-oriented API aimed at improving the readability of program source code. The name was coined by Eric Evans and Martin Fowler.
method chaining
A fluent interface is good because it simplifies making multiple method calls on a single object. This is usually implemented by method chaining, where each method passes the call context on to the next link (but a fluent interface entails something more than just a chain of methods ). Usually, this context is:
This style is indirectly useful because it makes code more readable and intuitive . However, it can be very detrimental to debugging if the chain acts as a single expression, where the debugger cannot always set an intermediate breakpoint.
A fluent interface is usually implemented using method chaining to achieve method cascading (in languages that do not natively support cascading), in particular because each method returns the object it is attached to, often called this or self. More abstractly, a fluent interface passes the context of the instruction on to the subsequent call in the method chain, where the context is usually
Note that a "fluent interface" means more than just method cascading through a chain; it entails designing an interface that reads like a DSL, using other techniques such as "nested functions and object scoping".
Starting with C# 3.5 and above, advanced ways of implementing a fluent interface were introduced:
namespace Example.FluentInterfaces
{
#region Standard Example
public interface IConfiguration
{
string Color { set; }
int Height { set; }
int Length { set; }
int Depth { set; }
}
public class Configuration : IConfiguration
{
string color;
int height;
int length;
int depth;
public string Color
{
set { color = value; }
}
public int Height
{
set { height = value; }
}
public int Length
{
set { length = value; }
}
public int Depth
{
set { depth = value; }
}
}
#endregion
#region Fluent Example
public interface IConfigurationFluent
{
IConfigurationFluent SetColor(string color);
IConfigurationFluent SetHeight(int height);
IConfigurationFluent SetLength(int length);
IConfigurationFluent SetDepth(int depth);
}
public class ConfigurationFluent : IConfigurationFluent
{
string color;
int height;
int length;
int depth;
public IConfigurationFluent SetColor(string color)
{
this.color = color;
return this;
}
public IConfigurationFluent SetHeight(int height)
{
this.height = height;
return this;
}
public IConfigurationFluent SetLength(int length)
{
this.length = length;
return this;
}
public IConfigurationFluent SetDepth(int depth)
{
this.depth = depth;
return this;
}
}
#endregion
public class ExampleProgram
{
public static void Main(string[] args)
{
// Regular example
IConfiguration config = new Configuration
{
Color = "blue",
Height = 1,
Length = 2,
Depth = 3
};
// Fluent interface example
IConfigurationFluent fluentConfig =
new ConfigurationFluent().SetColor("blue")
.SetHeight(1)
.SetLength(2)
.SetDepth(3);
}
}
}
A trivial example in C++ is the standard iostream, where fluency is provided by operator overloading.
An example of a fluent interface wrapper in C++:
// regular definition class GlutApp { private: int w_, h_, x_, y_, argc_, display_mode_; char **argv_; char *title_; public: GlutApp(int argc, char** argv) { argc_ = argc; argv_ = argv; } void setDisplayMode(int mode) { display_mode_ = mode; } int getDisplayMode() { return display_mode_; } void setWindowSize(int w, int h) { w_ = w; h_ = h; } void setWindowPosition(int x, int y) { x_ = x; y_ = y; } void setTitle(const char *title) { title_ = title; } void create(); }; // regular usage int main(int argc, char **argv) { GlutApp app(argc, argv); app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Set framebuffer params app.setWindowSize(500, 500); // Set window params app.setWindowPosition(200, 200); app.setTitle("My OpenGL/GLUT App"); app.create(); } // Fluent interface wrapper class FluentGlutApp : private GlutApp { public: FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // inherit the parent constructor FluentGlutApp &withDoubleBuffer() { setDisplayMode(getDisplayMode() | GLUT_DOUBLE); return *this; } FluentGlutApp &withRGBA() { setDisplayMode(getDisplayMode() | GLUT_RGBA); return *this; } FluentGlutApp &withAlpha() { setDisplayMode(getDisplayMode() | GLUT_ALPHA); return *this; } FluentGlutApp &withDepth() { setDisplayMode(getDisplayMode() | GLUT_DEPTH); return *this; } FluentGlutApp &across(int w, int h) { setWindowSize(w, h); return *this; } FluentGlutApp &at(int x, int y) { setWindowPosition(x, y); return *this; } FluentGlutApp &named(const char *title) { setTitle(title); return *this; } // it does not matter whether the chain continues after create(), so we do not return *this void create() { GlutApp::create(); } }; // using the fluent interface int main(int argc, char **argv) { FluentGlutApp app(argc, argv) .withDoubleBuffer().withRGBA().withAlpha().withDepth() .at(200, 200).across(500, 500) .named("My OpenGL/GLUT App"); app.create(); }
Some Java APIs implement this kind of interface, for example the Java Persistence API:
public Collection<Student> findByNameAgeGender(String name, int age, Gender gender) { return em.createNamedQuery("Student.findByNameAgeGender") .setParameter("name", name) .setParameter("age", age) .setParameter("gender", gender) .setFirstResult(1) .setMaxResults(30) .setHint("hintName", "hintValue") .getResultList(); }
The op4j library allows you to use a fluent interface for auxiliary tasks such as iterating over structures, converting data, filtering, and so on.
String[] datesStr = new String[] {"12-10-1492", "06-12-1978" }; ... List<Calendar> dates = Op.on(datesStr).toList().map(FnString.toCalendar("dd-MM-yyyy")).get();
The EasyMock mock-object testing library also makes extensive use of this style to provide a convenient interface.
Collection mockCollection = EasyMock.createMock(Collection.class); EasyMock.expect(mockCollection.remove(null)).andThrow(new NullPointerException()).atLeastOnce();
An example of a class with a fluent interface in PHP:
class Car {
private $speed, $color, $doors;
public function setSpeed($speed){
$this->speed = $speed;
return $this;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function setDoors($doors) {
$this->doors = $doors;
return $this;
}
}
// Regular implementation
$myCar2 = new Car();
$myCar2->setSpeed(100);
$myCar2->setColor('blue');
$myCar2->setDoors(5);
// Fluent interface
$myCar = new Car();
$myCar->setSpeed(100)->setColor('blue')->setDoors(5);
An example of a class with a fluent interface in JavaScript:
var Car = (function(){ var speed, color, doors, pub; function setSpeed(new_speed) { speed = new_speed; return pub; } function setColor(new_color) { color = new_color; return pub; } function setDoors(new_doors) { doors = new_doors; return pub; } pub = { 'setSpeed': setSpeed, 'setColor': setColor, 'setDoors': setDoors, }; return pub; }) // Regular implementation myCar2 = Car(); myCar2.setSpeed(100); myCar2.setColor('blue'); myCar2.setDoors(5); // Fluent interface myCar = Car(); myCar.setSpeed(100).setColor('blue').setDoors(5);
A different approach can also be used:
var $ = function(selector) { if(this.$) { return new $(selector); } if(typeof selector == "string") { this.init = document.getElementById(selector); } }; $.prototype = { text: function(text) { if(!text){ this.init.innerHTML; } this.init.innerHTML = text; return this; }, css: function(style) { for(var i in style){ this.init.style[i] = style[i]; } return this; } }; // usage example: $('div').text('div').css({color: "red"});
An example of an implementation that does not depend on the type of the returned object:
({
foo: function (a) {
return a;
}
}).foo('foo').toUpperCase();
|
Design patterns
|
|
|---|---|
| Fundamental |
|
| Creational |
|
| Structural |
|
| Behavioral |
|
| Concurrency patterns |
|
| Architectural |
|
| Java EE patterns |
|
| Books |
|
| People |
|
Comments