Fluent interface
In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code.
A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining [1]). Generally, the context is
- defined through the return value of a called method
- self referential, where the new context is equivalent to the last context
- terminated through the return of a void context.
This style is marginally beneficial in readability due to its ability to provide a more fluid feel to the code [citation needed] however can be highly detrimental to debugging, as a fluent chain constitutes a single statement, in which debuggers may not allow setting up intermediate breakpoints for instance.
Examples
Delphi (Object Pascal)
The following example shows a class implementing a non-fluent interface, another class implementing a fluent counterpart of the aforementioned non-fluent interface, and differences in usage. The example is written in Delphi Object Pascal:
unit FluentInterface;
interface
type
IConfiguration = interface
procedure SetColor(Color: string);
procedure SetHeight(height: integer);
procedure SetLength(length: integer);
procedure SetDepth(depth: integer);
end;
IConfigurationFluent = interface
function SetColor(Color: string): IConfigurationFluent;
function SetHeight(height: integer): IConfigurationFluent;
function SetLength(length: integer): IConfigurationFluent;
function SetDepth(depth: integer): IConfigurationFluent;
end;
TConfiguration = class(TInterfacedObject, IConfiguration)
private
FColor: string;
FHeight: integer;
FLength: integer;
FDepth: integer;
protected
procedure SetColor(Color: string);
procedure SetHeight(height: integer);
procedure SetLength(length: integer);
procedure SetDepth(depth: integer);
end;
TConfigurationFluent = class(TInterfacedObject, IConfigurationFluent)
private
FColor: string;
FHeight: integer;
FLength: integer;
FDepth: integer;
protected
function SetColor(Color: string): IConfigurationFluent;
function SetHeight(height: integer): IConfigurationFluent;
function SetLength(length: integer): IConfigurationFluent;
function SetDepth(depth: integer): IConfigurationFluent;
public
class function New: IConfigurationFluent;
end;
implementation
procedure TConfiguration.SetColor(Color: string);
begin
FColor := Color;
end;
procedure TConfiguration.SetDepth(depth: integer);
begin
FDepth := depth;
end;
procedure TConfiguration.SetHeight(height: integer);
begin
FHeight := height;
end;
procedure TConfiguration.SetLength(length: integer);
begin
FLength := length;
end;
class function TConfigurationFluent.New: IConfigurationFluent;
begin
Result := Create;
end;
function TConfigurationFluent.SetColor(Color: string): IConfigurationFluent;
begin
FColor := Color;
Result := Self;
end;
function TConfigurationFluent.SetDepth(depth: integer): IConfigurationFluent;
begin
FDepth := depth;
Result := Self;
end;
function TConfigurationFluent.SetHeight(height: integer): IConfigurationFluent;
begin
FHeight := height;
Result := Self;
end;
function TConfigurationFluent.SetLength(length: integer): IConfigurationFluent;
begin
FLength := length;
Result := Self;
end;
end.
Basic Usage:
var C, D: IConfiguration;
E: IConfigurationFluent;
begin
C := TConfiguration.Create;
C.SetColor('blue');
C.SetHeight(1);
C.SetLength(2);
C.SetDepth(3);
{ same "non-fluent interface" accessed using the "with" statement }
D := TConfiguration.Create;
with D do begin
SetColor('blue');
SetHeight(1);
SetLength(2);
SetDepth(3)
end;
E := TConfigurationFluent.New
.SetColor('Blue')
.SetHeight(1)
.SetLength(2)
.SetDepth(3);
end;
C#
The following example shows a class implementing a non-fluent interface, another class implementing a fluent counterpart of the aforementioned non-fluent interface, and differences in usage. The example is written in C#:
namespace Example.FluentInterfaces
{
#region Standard Example
public interface IConfiguration
{
void SetColor(string newColor);
void SetDepth(int newDepth);
void SetHeight(int newHeight);
void SetLength(int newLength);
}
public sealed class Configuration : IConfiguration
{
private string color;
private int depth;
private int height;
private int length;
public void SetColor(string newColor)
{
this.color = newColor;
}
public void SetDepth(int newDepth)
{
this.depth = newDepth;
}
public void SetHeight(int newHeight)
{
this.height = newHeight;
}
public void SetLength(int newLength)
{
this.length = newLength;
}
}
#endregion
#region Fluent Example
public interface IConfigurationFluent
{
IConfigurationFluent SetColor(string newColor);
IConfigurationFluent SetDepth(int newDepth);
IConfigurationFluent SetHeight(int newHeight);
IConfigurationFluent SetLength(int newLength);
}
public sealed class ConfigurationFluent : IConfigurationFluent
{
private string color;
private int depth;
private int height;
private int length;
private ConfigurationFluent()
{
}
public static IConfigurationFluent New()
{
return new ConfigurationFluent();
}
public IConfigurationFluent SetColor(string newColor)
{
this.color = newColor;
return this;
}
public IConfigurationFluent SetDepth(int newDepth)
{
this.depth = newDepth;
return this;
}
public IConfigurationFluent SetHeight(int newHeight)
{
this.height = newHeight;
return this;
}
public IConfigurationFluent SetLength(int newLength)
{
this.length = newLength;
return this;
}
}
#endregion
public static class ExampleProgram
{
public static void Main()
{
// Standard Example
IConfiguration config = new Configuration();
config.SetColor("blue");
config.SetHeight(1);
config.SetLength(2);
config.SetDepth(3);
// Fluent Example
IConfigurationFluent fluentConfig =
ConfigurationFluent
.New()
.SetColor("blue")
.SetHeight(1)
.SetLength(2)
.SetDepth(3);
}
}
}
C++
A common use of the fluent interface in C++ is the standard iostream, which chains overloaded operators.
The following is an example of providing a fluent interface wrapper on top of a more traditional interface in C++:
// basic 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();
};
// basic 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 wrapper
class FluentGlutApp : private GlutApp {
public:
FluentGlutApp(int argc, char **argv) : GlutApp(argc, argv) {} // inherit 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 doesn't make sense to chain after create(), so don't return *this
void create() {
GlutApp::create();
}
};
// basic usage
int main(int argc, char **argv) {
FluentGlutApp(argc, argv)
.withDoubleBuffer().withRGBA().withAlpha().withDepth()
.at(200, 200).across(500, 500)
.named("My OpenGL/GLUT App")
.create();
}
Java
Some APIs in Java follow this practice, like 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 enables the use of fluent code for performing auxiliary tasks like structure iteration, data conversion, filtering, etc.
String[] datesStr = new String[] {"12-10-1492", "06-12-1978" };
...
List<Calendar> dates =
Op.on(datesStr).toList().map(FnString.toCalendar("dd-MM-yyyy")).get();
Also, the mock object testing library EasyMock makes extensive use of this style of interface to provide an expressive programming interface.
Collection mockCollection = EasyMock.createMock(Collection.class);
EasyMock.expect(mockCollection.remove(null)).andThrow(new NullPointerException()).atLeastOnce();
Another Java Example
In Java, the LayoutManager interface defines how Container objects can have controlled Component placement. One of the more powerful LayoutManager implementations is the GridBagLayout class which requires the use of the GridBagConstraints class to specify how layout control occurs. A typical example of the use of this class is something like the following.
GridBagLayout gl = new GridBagLayout();
JPanel p = new JPanel();
p.setLayout( gl );
JLabel l = new JLabel("Name:");
JTextField nm = new JTextField(10);
GridBagConstraints gc = new GridBagConstraints();
gc.gridx = 0;
gc.gridy = 0;
gc.fill = GridBagConstraints.NONE;
p.add( l, gc );
gc.gridx = 1;
gc.fill = GridBagConstraints.HORIZONTAL;
gc.weightx = 1;
p.add( nm, gc );
This creates a lot of code and makes it difficult to see what exactly is happening here. The Packer class, visible at http://packer.dev.java.net, provides a Fluent mechanism for using this class so that you would instead write:
JPanel p = new JPanel();
Packer pk = new Packer( p );
JLabel l = new JLabel("Name:");
JTextField nm = new JTextField(10);
pk.pack( l ).gridx(0).gridy(0);
pk.pack( nm ).gridx(1).gridy(0).fillx();
There are many places where Fluent APIs can greatly simplify how software is written and help create an API language that helps users be much more productive and comfortable with the API because the return value of a method always provides a context for further actions in that context.
PHP
The following is an example of a class with a fluent interface implemented in PHP:
class Car {
private $speed;
private $color;
private $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;
}
}
// Fluent interface
$myCar = new Car();
$myCar->setSpeed(100)->setColor('blue')->setDoors(5);
// Example without fluent interface
$myCar2 = new Car();
$myCar2->setSpeed(100);
$myCar2->setColor('blue');
$myCar2->setDoors(5);
JavaScript
The following is an example of a class with a fluent interface implemented 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;
};
// Fluent interface
myCar = Car();
myCar.setSpeed(100).setColor('blue').setDoors(5);
// Example without fluent interface
myCar2 = Car();
myCar2.setSpeed(100);
myCar2.setColor('blue');
myCar2.setDoors(5);
The following is a more concise code for the above example of a JavaScript class with a fluent interface.
var Car = function() {
var speed, color, doors;
this.setSpeed = function(speed) {
this.speed = speed;
return this;
};
this.setColor = function(color) {
this.color = color;
return this;
};
this.setDoors = function(doors) {
this.doors = doors;
return this;
};
};
// Fluent interface
myCar = new Car();
myCar.setSpeed(100).setColor('blue').setDoors(5);
// Example without fluent interface
myCar2 = new Car();
myCar2.setSpeed(100);
myCar2.setColor('blue');
myCar2.setDoors(5);