Misplaced Pages

Fluent interface: Difference between revisions

Article snapshot taken from Wikipedia with creative commons attribution-sharealike license. Give it a read and then ask your questions in the chat. We can research this topic together.
Browse history interactively← Previous editContent deleted Content addedVisualWikitext
Revision as of 08:48, 26 June 2019 edit62.90.169.124 (talk) External links← Previous edit Latest revision as of 11:14, 20 November 2024 edit undo188.192.202.120 (talk) PHP: Adds strict types and makes __toString more readable 
(47 intermediate revisions by 37 users not shown)
Line 1: Line 1:
{{Short description|Software engineering object-oriented API}}
{{about|the API design pattern|the user interface introduced in Microsoft Office 2007|Microsoft Office 2007#User interface|the 2017 Microsoft's visual design language|Fluent Design System}}
{{about|the API design pattern|the user interface introduced in Microsoft Office 2007|Microsoft Office 2007#User interface|Microsoft's 2017 visual design language|Fluent Design System}}
In ], a '''fluent interface''' (as first coined by ] and ]) is a method for designing ] APIs based extensively on ] with the goal of making the readability of the source code close to that of ordinary written ], essentially creating a ] within the interface. An example of a fluent test expectation in the JMock testing framework is:<ref name=fowler2005>, Martin Fowler, 20 December 2005</ref>


In ], a '''fluent interface''' is an ] ] whose design relies extensively on ]. Its goal is to increase code legibility by creating a ] (DSL). The term was coined in 2005 by ] and ].<ref name=fowler2005/>
<source lang=Java>
mock.expects(once()).method("m").with( or(stringContains("hello"),
stringContains("howdy")) );
</source>


==Implementation== ==Implementation==
A fluent interface is normally implemented by using ] to implement ] (in languages that do not natively support cascading), concretely by having each method return <code>]</code> (<code>self</code>). Stated more abstractly, a fluent interface relays the instruction context of a subsequent call in method chaining, where generally the context is A fluent interface is normally implemented by using ] to implement ] (in languages that do not natively support cascading), concretely by having each method return the object to which it is attached{{Citation needed|date=August 2024}}, often referred to as <code>]</code> or <code>self</code>. Stated more abstractly, a fluent interface relays the instruction context of a subsequent call in method chaining, where generally the context is
* defined through the return value of a called method * Defined through the return value of a called method
* self-referential, where the new context is equivalent to the last context * ], where the new context is equivalent to the last context
* terminated through the return of a void context. * Terminated through the return of a ] context


Note that a "fluent interface" means more than just method cascading via chaining; it entails designing an interface that reads like a DSL, using other techniques like "nested functions and object scoping".<ref name=fowler2005 /> Note that a "fluent interface" means more than just method cascading via chaining; it entails designing an interface that reads like a DSL, using other techniques like "nested functions and object scoping".<ref name=fowler2005 />


==History== ==History==
The term "fluent interface" was coined in late 2005, though this overall style of interface dates to the invention of ] in Smalltalk in the 1970s, and numerous examples in the 1980s. A common example is the ] library in C++, which uses the <code>&lt;&lt;</code> or <code>&gt;&gt;</code> ] for the message passing, sending multiple data to the same object and allowing "manipulators" for other method calls. Other early examples include the (from 1988 in Lisp) and the (from 1994 in C++) which used this style for object creation and property assignment. The term "fluent interface" was coined in late 2005, though this overall style of interface dates to the invention of ] in ] in the 1970s, and numerous examples in the 1980s. A common example is the ] library in ], which uses the <code>&lt;&lt;</code> or <code>&gt;&gt;</code> ] for the message passing, sending multiple data to the same object and allowing "manipulators" for other method calls. Other early examples include the Garnet system (from 1988 in Lisp) and the Amulet system (from 1994 in C++) which used this style for object creation and property assignment.


==Examples== ==Examples==
Line 22: Line 19:
===C#=== ===C#===


C# uses fluent programming extensively in ] to build queries using the ''standard query operators''. The implementation is based on ]s. ] uses fluent programming extensively in ] to build queries using "standard query operators". The implementation is based on ]s.


<source lang="csharp"> <syntaxhighlight lang="csharp">
var translations = new Dictionary<string, string> var translations = new Dictionary<string, string>
{
{
{"cat", "chat"}, {"cat", "chat"},
{"dog", "chien"}, {"dog", "chien"},
{"fish", "poisson"}, {"fish", "poisson"},
{"bird", "oiseau"} {"bird", "oiseau"}
};
};


// Find translations for English words containing the letter "a", // Find translations for English words containing the letter "a",
// sorted by length and displayed in uppercase // sorted by length and displayed in uppercase
IEnumerable<string> query = translations IEnumerable<string> query = translations
.Where (t => t.Key.Contains("a")) .Where(t => t.Key.Contains("a"))
.OrderBy (t => t.Value.Length) .OrderBy(t => t.Value.Length)
.Select (t => t.Value.ToUpper()); .Select(t => t.Value.ToUpper());


// The same query constructed progressively: // The same query constructed progressively:
var filtered = translations.Where (t => t.Key.Contains("a")); var filtered = translations.Where(t => t.Key.Contains("a"));
var sorted = filtered.OrderBy (t => t.Value.Length); var sorted = filtered.OrderBy (t => t.Value.Length);
var finalQuery = sorted.Select (t => t.Value.ToUpper()); var finalQuery = sorted.Select (t => t.Value.ToUpper());
</syntaxhighlight>
</source>
Fluent interface can also be used to chain a set of method, which operates/shares the same object. Like instead of creating a customer class we can create a data context which can be decorated with fluent interface as follows. Fluent interface can also be used to chain a set of method, which operates/shares the same object. Instead of creating a customer class, we can create a data context which can be decorated with fluent interface as follows.
<source lang="csharp"> <syntaxhighlight lang="csharp">
// Defines the data context // Defines the data context
class Context class Context
Line 88: Line 85:
public void Print() public void Print()
{ {
Console.WriteLine("First name: {0} \nLast name: {1} \nSex: {2} \nAddress: {3}", _context.FirstName, _context.LastName, _context.Sex, _context.Address); Console.WriteLine($"First name: {_context.FirstName} \nLast name: {_context.LastName} \nSex: {_context.Sex} \nAddress: {_context.Address}");
} }
} }
Line 102: Line 99:
} }
} }
</syntaxhighlight>
</source>

The ] testing framework ] uses a mix of C#'s ] and ] in a fluent style to construct its "constraint based" ]:
<syntaxhighlight lang="csharp">
Assert.That(() => 2 * 2, Is.AtLeast(3).And.AtMost(5));
</syntaxhighlight>


===C++=== ===C++===
Line 109: Line 111:
The following is an example of providing a fluent interface wrapper on top of a more traditional interface in C++: The following is an example of providing a fluent interface wrapper on top of a more traditional interface in C++:


<source lang="cpp"> <syntaxhighlight lang="cpp">
// Basic definition // Basic definition
class GlutApp { class GlutApp {
Line 195: Line 197:
.create(); .create();
} }
</syntaxhighlight>
</source>


===Java=== ===Java===
An example of a fluent test expectation in the jMock testing framework is:<ref name=fowler2005>], "", 20 December 2005</ref>
The ] library models SQL as a fluent API in Java


<source lang="java"> <syntaxhighlight lang=Java>
mock.expects(once()).method("m").with( or(stringContains("hello"),
stringContains("howdy")) );
</syntaxhighlight>
The ] library models SQL as a fluent API in Java:
<syntaxhighlight lang="java">
Author author = AUTHOR.as("author"); Author author = AUTHOR.as("author");
create.selectFrom(author) create.selectFrom(author)
Line 207: Line 214:
.where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT)) .where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT))
.and(BOOK.AUTHOR_ID.eq(author.ID)))); .and(BOOK.AUTHOR_ID.eq(author.ID))));
</syntaxhighlight>
</source>


The annotation processor enables the creation of a fluent API using Java annotations. The fluflu annotation processor enables the creation of a fluent API using Java annotations.


The library enables Java 8 Lambdas to be represented as objects in the form of at runtime, making it possible to create type-safe fluent interfaces, i.e. instead of: The JaQue library enables Java 8 Lambdas to be represented as objects in the form of ] at runtime, making it possible to create type-safe fluent interfaces, i.e., instead of:
<source lang="java"> <syntaxhighlight lang="java">
Customer obj = ... Customer obj = ...
obj.property("name").eq("John") obj.property("name").eq("John")
</syntaxhighlight>
</source>
One can write: One can write:
<source lang="java"> <syntaxhighlight lang="java">
method<Customer>(customer -> customer.getName() == "John") method<Customer>(customer -> customer.getName() == "John")
</syntaxhighlight>
</source>


Also, the ] testing library makes extensive use of this style of interface to provide an expressive programming interface. Also, the ] testing library EasyMock makes extensive use of this style of interface to provide an expressive programming interface.
<source lang="java"> <syntaxhighlight lang="java">
Collection mockCollection = EasyMock.createMock(Collection.class); Collection mockCollection = EasyMock.createMock(Collection.class);
EasyMock EasyMock
Line 228: Line 235:
.andThrow(new NullPointerException()) .andThrow(new NullPointerException())
.atLeastOnce(); .atLeastOnce();
</syntaxhighlight>
</source>


In the Java Swing API, 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. In the Java Swing API, the LayoutManager interface defines how Container objects can have controlled Component placement. One of the more powerful <code>LayoutManager</code> implementations is the GridBagLayout class which requires the use of the <code>GridBagConstraints</code> class to specify how layout control occurs. A typical example of the use of this class is something like the following.
<source lang="java"> <syntaxhighlight lang="java">
GridBagLayout gl = new GridBagLayout(); GridBagLayout gl = new GridBagLayout();
JPanel p = new JPanel(); JPanel p = new JPanel();
Line 249: Line 256:
gc.weightx = 1; gc.weightx = 1;
p.add( nm, gc ); p.add( nm, gc );
</syntaxhighlight>
</source>

This creates a lot of code and makes it difficult to see what exactly is happening here. The Packer class, visible at http://java.net/projects/packer/, provides a Fluent mechanism for using this class so that you would instead write:
This creates a lot of code and makes it difficult to see what exactly is happening here. The <code>Packer</code> class provides a fluent mechanism, so you would instead write:<ref>{{cite web |title=Interface Pack200.Packer |url=https://docs.oracle.com/javase/8/docs/api/java/util/jar/Pack200.Packer.html |website=Oracle |accessdate=13 November 2019}}</ref>
<source lang="java">
<syntaxhighlight lang="java">
JPanel p = new JPanel(); JPanel p = new JPanel();
Packer pk = new Packer( p ); Packer pk = new Packer( p );
Line 260: Line 268:
pk.pack( l ).gridx(0).gridy(0); pk.pack( l ).gridx(0).gridy(0);
pk.pack( nm ).gridx(1).gridy(0).fillx(); pk.pack( nm ).gridx(1).gridy(0).fillx();
</syntaxhighlight>
</source>

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.
There are many places where fluent APIs can 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.


===JavaScript=== ===JavaScript===


There are many examples of JavaScript libraries that use some variant of this: ] probably being the most well known. Typically fluent builders are used to implement 'database queries', for example in https://github.com/Medium/dynamite : There are many examples of JavaScript libraries that use some variant of this: ] probably being the most well known. Typically, fluent builders are used to implement "database queries", for example in the Dynamite client library:


<source lang="javascript"> <syntaxhighlight lang="javascript">
// getting an item from a table // getting an item from a table
client.getItem('user-table') client.getItem('user-table')
Line 276: Line 285:
// data.result: the resulting object // data.result: the resulting object
}) })
</syntaxhighlight>
</source>


A simple way to do this in JavaScript is using prototype inheritance and `this`. A simple way to do this in JavaScript is using prototype inheritance and <code>this</code>.


<source lang="javascript"> <syntaxhighlight lang="javascript">
// example from https://schier.co/blog/2013/11/14/method-chaining-in-javascript.html // example from https://schier.co/blog/2013/11/14/method-chaining-in-javascript.html
// define the class


class Kitten { class Kitten {
Line 288: Line 296:
this.name = 'Garfield'; this.name = 'Garfield';
this.color = 'orange'; this.color = 'orange';
this.gender = 'male';
} }


Line 298: Line 305:
setColor(color) { setColor(color) {
this.color = color; this.color = color;
return this;
}

setGender(gender) {
this.gender = gender;
return this; return this;
} }
Line 308: Line 310:
save() { save() {
console.log( console.log(
`saving ${this.name}, the ${this.color} ${this.gender} kitten` `saving ${this.name}, the ${this.color} kitten`
); );

// save to database
return this; return this;
} }
Line 321: Line 320:
.setName('Salem') .setName('Salem')
.setColor('black') .setColor('black')
.setGender('male')
.save(); .save();
</syntaxhighlight>
</source>


===Scala=== ===Scala===


Scala supports a fluent syntax for both method calls and class mixins, using '''trait'''s and the '''with''' keyword. For example: ] supports a fluent syntax for both method calls and class ]s, using traits and the <code>with</code> keyword. For example:


<source lang="scala"> <syntaxhighlight lang="scala">
class Color { def rgb(): Tuple3 } class Color { def rgb(): Tuple3 }
object Black extends Color { override def rgb(): Tuple3 = ("0", "0", "0"); } object Black extends Color { override def rgb(): Tuple3 = ("0", "0", "0"); }
Line 363: Line 361:
val appWin = new SwingWindow() with WindowBorder val appWin = new SwingWindow() with WindowBorder
appWin.render() appWin.render()
</syntaxhighlight>
</source>


===Perl 6=== ===Raku===


In ], there are many approaches, but one of the simplest is to declare attributes as read/write and use the <code>given</code> keyword. The type annotations are optional, but the native ] makes it much safer to write directly to public attributes. In ], there are many approaches, but one of the simplest is to declare attributes as read/write and use the <code>given</code> keyword. The type annotations are optional, but the native ] makes it much safer to write directly to public attributes.


<source lang="perl6"> <syntaxhighlight lang="perl6">
class Employee { class Employee {
subset Salary of Real where * > 0; subset Salary of Real where * > 0;
Line 400: Line 398:
# Surname: Ride # Surname: Ride
# Salary: 200 # Salary: 200
</syntaxhighlight>
</source>


===PHP=== ===PHP===


In PHP, one can return the current object by using the <code>$this</code> special variable which represent the instance. Hence <code>return $this;</code> will make the method return the instance. The example below defines a class Employee and three methods to set its name, surname and salary. Each return the instance of the Employee class allowing to chain methods. In ], one can return the current object by using the <code>$this</code> special variable which represent the instance. Hence <code>return $this;</code> will make the method return the instance. The example below defines a class <code>Employee</code> and three methods to set its name, surname and salary. Each return the instance of the <code>Employee</code> class allowing to chain methods.


<source lang="php"> <syntaxhighlight lang="php">
<?php declare(strict_types=1);
<?php

class Employee
final class Employee
{ {
public $name; private string $name;
public $surName; private string $surname;
public $salary; private string $salary;


public function setName($name) public function setName(string $name): self
{ {
$this->name = $name; $this->name = $name;
Line 421: Line 420:
} }


public function setSurname($surname) public function setSurname(string $surname): self
{ {
$this->surName = $surname; $this->surname = $surname;


return $this; return $this;
} }


public function setSalary($salary) public function setSalary(string $salary): self
{ {
$this->salary = $salary; $this->salary = $salary;
Line 435: Line 434:
} }


public function __toString() public function __toString(): string
{ {
return <<<INFO
$employeeInfo = 'Name: ' . $this->name . PHP_EOL;
$employeeInfo .= 'Surname: ' . $this->surName . PHP_EOL; Name: {$this->name}
$employeeInfo .= 'Salary: ' . $this->salary . PHP_EOL; Surname: {$this->surname}
Salary: {$this->salary}

return $employeeInfo; INFO;
} }
} }
Line 447: Line 446:
# Create a new instance of the Employee class, Tom Smith, with a salary of 100: # Create a new instance of the Employee class, Tom Smith, with a salary of 100:
$employee = (new Employee()) $employee = (new Employee())
->setName('Tom') ->setName('Tom')
->setSurname('Smith') ->setSurname('Smith')
->setSalary('100'); ->setSalary('100');


# Display the value of the Employee instance: # Display the value of the Employee instance:
Line 458: Line 457:
# Surname: Smith # Surname: Smith
# Salary: 100 # Salary: 100
</syntaxhighlight>
</source>


===Python=== ===Python===
In Python, returning <code>self</code> in the instance method is one way to implement the fluent pattern. In ], returning <code>self</code> in the instance method is one way to implement the fluent pattern.


It is however discouraged by the language’s creator, Guido van Rossum,<ref>{{Cite web|last=Rossum|first=Guido van|date=October 17, 2003|title= sort() return value|url=https://mail.python.org/pipermail/python-dev/2003-October/038855.html|access-date=2022-02-01}}</ref> and therefore considered unpythonic (not idiomatic) for operations that do not return new values. Van Rossum provides string processing operations as example where he sees the fluent pattern appropriate.
<source lang="python">
<syntaxhighlight lang="python">
class Poem():
class Poem:
def __init__(self, title):
def __init__(self, title: str) -> None:
self.title = title self.title = title


def indent(self, spaces): def indent(self, spaces: int):
"""Indent the poem with the specified number of spaces."""
self.title = " " * spaces + self.title self.title = " " * spaces + self.title
return self return self


def suffix(self, author): def suffix(self, author: str):
self.title = self.title + " - " + author """Suffix the poem with the author name."""
self.title = f"{self.title} - {author}"
return self return self
</syntaxhighlight>
</source>
<source lang="pycon"> <syntaxhighlight lang="pycon">
>>> Poem("Road Not Travelled").indent(4).suffix("Robert Frost").content >>> Poem("Road Not Travelled").indent(4).suffix("Robert Frost").title
' Road Not Travelled - Robert Frost' ' Road Not Travelled - Robert Frost'
</syntaxhighlight>
</source>


===Swift=== ===Swift===
In Swift 3.0+ returning <code>self</code> in the functions is one way to implement the fluent pattern. In ] 3.0+ returning <code>self</code> in the functions is one way to implement the fluent pattern.


<source lang="swift"> <syntaxhighlight lang="swift">
class Person { class Person {
var firstname: String = "" var firstname: String = ""
Line 508: Line 510:
} }
} }
</syntaxhighlight>
</source>


<source lang="swift"> <syntaxhighlight lang="swift">
let person = Person() let person = Person()
.set(firstname: "John") .set(firstname: "John")
.set(lastname: "Doe") .set(lastname: "Doe")
.set(favoriteQuote: "I like turtles") .set(favoriteQuote: "I like turtles")
</syntaxhighlight>
</source>


==Immutability==
=== Progress ABL ===
<syntaxhighlight lang="abl">
/*------------------------------------------------------------------------
File : Chain
Purpose : Attempt to use chaining components
Syntax :
Description : Attempt to use chaining methods
Author(s) : Scott Auge
Created : Wed Oct 31 10:36:39 EDT 2018
Notes : "Fluent" object or "Method Cascading"
----------------------------------------------------------------------*/


It's possible to create ] fluent interfaces that utilise ] semantics. In this variation of the pattern, instead of modifying internal properties and returning a reference to the same object, the object is instead cloned, with properties changed on the cloned object, and that object returned.
USING Progress.Lang.*.


The benefit of this approach is that the interface can be used to create configurations of objects that can fork off from a particular point; Allowing two or more objects to share a certain amount of state, and be used further without interfering with each other.
BLOCK-LEVEL ON ERROR UNDO, THROW.


===JavaScript example===
CLASS Chain:
// Prove the same object is being updated.


Using copy-on-write semantics, the JavaScript example from above becomes:
DEFINE PROPERTY V AS CHARACTER
GET.
SET.


<syntaxhighlight lang="javascript">
METHOD PUBLIC Chain Item1(INPUT A AS CHARACTER ):
class Kitten {
DISP "Item1".
constructor() {
V = V + A.
this.name = 'Garfield';
RETURN THIS-OBJECT.
this.color = 'orange';
END.
}
METHOD PUBLIC Chain Item2(INPUT B AS CHARACTER ):
DISP "Item2".
V = V + B.
RETURN THIS-OBJECT.
END.
END CLASS.
</syntaxhighlight>And the use of the class<syntaxhighlight lang="abl">
DEFINE VARIABLE Chain AS Chain NO-UNDO.


setName(name) {
Chain = NEW Chain().
const copy = new Kitten();
copy.color = this.color;
copy.name = name;
return copy;
}


setColor(color) {
// Show the order of execution (left to right)
const copy = new Kitten();
copy.name = this.name;
copy.color = color;
return copy;
}

// ...
}

// use it
const kitten1 = new Kitten()
.setName('Salem');


const kitten2 = kitten1
Chain:Item1("This")
.setColor('black');
:Item2("That").


console.log(kitten1, kitten2);
DISP Chain:V.
// -> Kitten({ name: 'Salem', color: 'orange' }), Kitten({ name: 'Salem', color: 'black' })
</syntaxhighlight> </syntaxhighlight>


==Problems== ==Problems==
=== Errors can not be captured at compile time === === Errors cannot be captured at compile time ===
In typed languages using a constructor requiring all parameters will fail at compilation time while the fluent approach will only be able to generate run-time errors, missing all the type-safety checks of modern compilers. It also contradicts the "]" approach for error protection. In typed languages, using a constructor requiring all parameters will fail at compilation time while the fluent approach will only be able to generate ] errors, missing all the type-safety checks of modern compilers. It also contradicts the "]" approach for error protection.


===Debugging & error reporting=== ===Debugging and error reporting===


Single-line chained statements may be more difficult to debug as debuggers may not be able to set breakpoints within the chain. Stepping through a single-line statement in a debugger may also be less convenient. Single-line chained statements may be more difficult to debug as debuggers may not be able to set breakpoints within the chain. Stepping through a single-line statement in a debugger may also be less convenient.
<source lang="java"> <syntaxhighlight lang="java">
java.nio.ByteBuffer.allocate(10).rewind().limit(100); java.nio.ByteBuffer.allocate(10).rewind().limit(100);
</syntaxhighlight>
</source>

Another issue is that it may not be clear which of the method calls caused an exception, in particular if there are multiple calls to the same method. These issues can be overcome by breaking the statement into multiple lines which preserves readability while allowing the user to set breakpoints within the chain and to easily step through the code line by line: Another issue is that it may not be clear which of the method calls caused an exception, in particular if there are multiple calls to the same method. These issues can be overcome by breaking the statement into multiple lines which preserves readability while allowing the user to set breakpoints within the chain and to easily step through the code line by line:

<source lang="java">
<syntaxhighlight lang="java">
java.nio.ByteBuffer java.nio.ByteBuffer
.allocate(10) .allocate(10)
.rewind() .rewind()
.limit(100); .limit(100);
</syntaxhighlight>
</source>
However, some debuggers always show the first line in the exception backtrace, although the exception has been thrown on any line. However, some debuggers always show the first line in the exception backtrace, although the exception has been thrown on any line.


===Logging=== ===Logging===
Adding logging into the middle of a chain of fluent calls can be an issue. E.g., given:
One more issue is with adding log statements.
<source lang="java"> <syntaxhighlight lang="java">
ByteBuffer buffer = ByteBuffer.allocate(10).rewind().limit(100); ByteBuffer buffer = ByteBuffer.allocate(10).rewind().limit(100);
</syntaxhighlight>
</source>
E.g. to log the state of <code>buffer</code> after <code>rewind()</code> method call, it is necessary to break the fluent calls: To log the state of <code>buffer</code> after the <code>rewind()</code> method call, it is necessary to break the fluent calls:
<source lang="java"> <syntaxhighlight lang="java">
ByteBuffer buffer = ByteBuffer.allocate(10).rewind(); ByteBuffer buffer = ByteBuffer.allocate(10).rewind();
log.debug("First byte after rewind is " + buffer.get(0)); log.debug("First byte after rewind is " + buffer.get(0));
buffer.limit(100); buffer.limit(100);
</syntaxhighlight>
</source>


This can be worked-around in languages that support ] by defining a new extension to wrap the desired logging functionality, for example in C# (using the same Java `ByteBuffer` example as above) This can be worked around in languages that support ] by defining a new extension to wrap the desired logging functionality, for example in C# (using the same Java ByteBuffer example as above):


<source lang="csharp"> <syntaxhighlight lang="csharp">
static class ByteBufferExtensions static class ByteBufferExtensions
{ {
public static ByteBuffer Log(this ByteBuffer buffer, Log log, Action<ByteBuffer> getMessage) public static ByteBuffer Log(this ByteBuffer buffer, Log log, Action<ByteBuffer> getMessage)
{ {
string message = getMessage( buffer ); string message = getMessage(buffer);
log.debug( message ); log.debug(message);
return buffer; return buffer;
} }
Line 619: Line 617:
.Limit(100); .Limit(100);


</syntaxhighlight>
</source>


=== Subclasses === === Subclasses ===
Subclasses in ] languages (C++, Java, C#, etc.) often have to override all methods from their superclass that participate in a fluent interface in order to change their return type. For example, in Java: Subclasses in ] languages (C++, Java, C#, etc.) often have to override all methods from their superclass that participate in a fluent interface in order to change their return type. For example:
<source lang="java"> <syntaxhighlight lang="java">
class A { class A {
public A doThis() { ... } public A doThis() { ... }
Line 634: Line 632:
A a = new B().doThat().doThis(); // This would work even without overriding A.doThis(). A a = new B().doThat().doThis(); // This would work even without overriding A.doThis().
B b = new B().doThis().doThat(); // This would fail if A.doThis() wasn't overridden. B b = new B().doThis().doThat(); // This would fail if A.doThis() wasn't overridden.
</syntaxhighlight>
</source>


Languages that are capable of expressing ] can use it to avoid this difficulty. E. g. in Java: Languages that are capable of expressing ] can use it to avoid this difficulty. For example:


<source lang="java"> <syntaxhighlight lang="java">
abstract class AbstractA<T extends AbstractA<T>> { abstract class AbstractA<T extends AbstractA<T>> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Line 652: Line 650:
B b = new B().doThis().doThat(); // Works! B b = new B().doThis().doThat(); // Works!
A a = new A().doThis(); // Also works. A a = new A().doThis(); // Also works.
</syntaxhighlight>
</source>


Note that in order to be able to create instances of the parent class, we had to split it into two classes — <code>AbstractA</code> and <code>A</code>, the latter with no content (it would only contain constructors if those were needed). The approach can easily be extended if we want to have sub-subclasses (etc.) too: Note that in order to be able to create instances of the parent class, we had to split it into two classes — <code>AbstractA</code> and <code>A</code>, the latter with no content (it would only contain constructors if those were needed). The approach can easily be extended if we want to have sub-subclasses (etc.) too:


<source lang="java"> <syntaxhighlight lang="java">
abstract class AbstractB<T extends AbstractB<T>> extends AbstractA<T> { abstract class AbstractB<T extends AbstractB<T>> extends AbstractA<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Line 671: Line 669:
C c = new C().doThis().doThat().foo(); // Works! C c = new C().doThis().doThat().foo(); // Works!
B b = new B().doThis().doThat(); // Still works. B b = new B().doThis().doThat(); // Still works.
</source>In a dependently typed language, e.g. Scala, methods can also be explicitly defined as always returning <code>this</code> and thus can be defined only once for subclasses to take advantage of the fluent interface:<syntaxhighlight lang="scala"> </syntaxhighlight>In a dependently typed language, e.g. Scala, methods can also be explicitly defined as always returning <code>this</code> and thus can be defined only once for subclasses to take advantage of the fluent interface:<syntaxhighlight lang="scala">
class A { class A {
def doThis(): this.type = { ... } // returns this, and always this. def doThis(): this.type = { ... } // returns this, and always this.
Line 697: Line 695:
* *
* *
* * {{Webarchive|url=https://web.archive.org/web/20171223050720/http://tnvalidate.codeplex.com/ |date=2017-12-23 }}
* *
* *

Latest revision as of 11:14, 20 November 2024

Software engineering object-oriented API This article is about the API design pattern. For the user interface introduced in Microsoft Office 2007, see Microsoft Office 2007 § User interface. For Microsoft's 2017 visual design language, see Fluent Design System.

In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler.

Implementation

A fluent interface is normally implemented by using method chaining to implement method cascading (in languages that do not natively support cascading), concretely by having each method return the object to which it is attached, often referred to as this or self. Stated more abstractly, a fluent interface relays the instruction context of a subsequent call in method chaining, where 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

Note that a "fluent interface" means more than just method cascading via chaining; it entails designing an interface that reads like a DSL, using other techniques like "nested functions and object scoping".

History

The term "fluent interface" was coined in late 2005, though this overall style of interface dates to the invention of method cascading in Smalltalk in the 1970s, and numerous examples in the 1980s. A common example is the iostream library in C++, which uses the << or >> operators for the message passing, sending multiple data to the same object and allowing "manipulators" for other method calls. Other early examples include the Garnet system (from 1988 in Lisp) and the Amulet system (from 1994 in C++) which used this style for object creation and property assignment.

Examples

C#

C# uses fluent programming extensively in LINQ to build queries using "standard query operators". The implementation is based on extension methods.

var translations = new Dictionary<string, string>
{
    {"cat", "chat"},
    {"dog", "chien"},
    {"fish", "poisson"},
    {"bird", "oiseau"}
};
// Find translations for English words containing the letter "a",
// sorted by length and displayed in uppercase
IEnumerable<string> query = translations
	.Where(t => t.Key.Contains("a"))
	.OrderBy(t => t.Value.Length)
	.Select(t => t.Value.ToUpper());
// The same query constructed progressively:
var filtered   = translations.Where(t => t.Key.Contains("a"));
var sorted     = filtered.OrderBy  (t => t.Value.Length);
var finalQuery = sorted.Select     (t => t.Value.ToUpper());

Fluent interface can also be used to chain a set of method, which operates/shares the same object. Instead of creating a customer class, we can create a data context which can be decorated with fluent interface as follows.

// Defines the data context
class Context
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Sex { get; set; }
    public string Address { get; set; }
}
class Customer
{
    private Context _context = new Context(); // Initializes the context
    // set the value for properties
    public Customer FirstName(string firstName)
    {
        _context.FirstName = firstName;
        return this;
    }
    public Customer LastName(string lastName)
    {
        _context.LastName = lastName;
        return this;
    }
    public Customer Sex(string sex)
    {
        _context.Sex = sex;
        return this;
    }
    public Customer Address(string address)
    {
        _context.Address = address;
        return this;
    }
    // Prints the data to console
    public void Print()
    {
        Console.WriteLine($"First name: {_context.FirstName} \nLast name: {_context.LastName} \nSex: {_context.Sex} \nAddress: {_context.Address}");
    }
}
class Program
{
    static void Main(string args)
    {
        // Object creation
        Customer c1 = new Customer();
        // Using the method chaining to assign & print data with a single line
        c1.FirstName("vinod").LastName("srivastav").Sex("male").Address("bangalore").Print();
    }
}

The .NET testing framework NUnit uses a mix of C#'s methods and properties in a fluent style to construct its "constraint based" assertions:

Assert.That(() => 2 * 2, Is.AtLeast(3).And.AtMost(5));

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();
     }
 };
 // Fluent 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

An example of a fluent test expectation in the jMock testing framework is:

mock.expects(once()).method("m").with( or(stringContains("hello"),
                                          stringContains("howdy")) );

The jOOQ library models SQL as a fluent API in Java:

Author author = AUTHOR.as("author");
create.selectFrom(author)
      .where(exists(selectOne()
                   .from(BOOK)
                   .where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT))
                   .and(BOOK.AUTHOR_ID.eq(author.ID))));

The fluflu annotation processor enables the creation of a fluent API using Java annotations.

The JaQue library enables Java 8 Lambdas to be represented as objects in the form of expression trees at runtime, making it possible to create type-safe fluent interfaces, i.e., instead of:

Customer obj = ...
obj.property("name").eq("John")

One can write:

method<Customer>(customer -> customer.getName() == "John")

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

In the Java Swing API, 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 provides a fluent mechanism, so 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 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.

JavaScript

There are many examples of JavaScript libraries that use some variant of this: jQuery probably being the most well known. Typically, fluent builders are used to implement "database queries", for example in the Dynamite client library:

// getting an item from a table
client.getItem('user-table')
    .setHashKey('userId', 'userA')
    .setRangeKey('column', '@')
    .execute()
    .then(function(data) {
        // data.result: the resulting object
    })

A simple way to do this in JavaScript is using prototype inheritance and this.

// example from https://schier.co/blog/2013/11/14/method-chaining-in-javascript.html
class Kitten {
  constructor() {
    this.name = 'Garfield';
    this.color = 'orange';
  }
  setName(name) {
    this.name = name;
    return this;
  }
  setColor(color) {
    this.color = color;
    return this;
  }
  save() {
    console.log(
      `saving ${this.name}, the ${this.color} kitten`
    );
    return this;
  }
}
// use it
new Kitten()
  .setName('Salem')
  .setColor('black')
  .save();

Scala

Scala supports a fluent syntax for both method calls and class mixins, using traits and the with keyword. For example:

class Color { def rgb(): Tuple3 }
object Black extends Color { override def rgb(): Tuple3 = ("0", "0", "0"); }
trait GUIWindow {
  // Rendering methods that return this for fluent drawing
  def set_pen_color(color: Color): this.type
  def move_to(pos: Position): this.type
  def line_to(pos: Position, end_pos: Position): this.type
  def render(): this.type = this // Don't draw anything, just return this, for child implementations to use fluently
  def top_left(): Position
  def bottom_left(): Position
  def top_right(): Position
  def bottom_right(): Position
}
trait WindowBorder extends GUIWindow {
  def render(): GUIWindow = {
    super.render()
      .move_to(top_left())
      .set_pen_color(Black)
      .line_to(top_right())
      .line_to(bottom_right())
      .line_to(bottom_left())
      .line_to(top_left())
   }
}
class SwingWindow extends GUIWindow { ... }
val appWin = new SwingWindow() with WindowBorder
appWin.render()

Raku

In Raku, there are many approaches, but one of the simplest is to declare attributes as read/write and use the given keyword. The type annotations are optional, but the native gradual typing makes it much safer to write directly to public attributes.

class Employee {
    subset Salary         of Real where * > 0;
    subset NonEmptyString of Str  where * ~~ /\S/; # at least one non-space character
    has NonEmptyString $.name    is rw;
    has NonEmptyString $.surname is rw;
    has Salary         $.salary  is rw;
    method gist {
        return qq:to;
        Name:    $.name
        Surname: $.surname
        Salary:  $.salary
        END
    }
}
my $employee = Employee.new();
given $employee {
    .name    = 'Sally';
    .surname = 'Ride';
    .salary  = 200;
}
say $employee;
# Output:
# Name:    Sally
# Surname: Ride
# Salary:  200

PHP

In PHP, one can return the current object by using the $this special variable which represent the instance. Hence return $this; will make the method return the instance. The example below defines a class Employee and three methods to set its name, surname and salary. Each return the instance of the Employee class allowing to chain methods.

<?php declare(strict_types=1);
final class Employee
{
    private string $name;
    private string $surname; 
    private string $salary;
    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }
    public function setSurname(string $surname): self
    {
        $this->surname = $surname;
        return $this;
    }
    public function setSalary(string $salary): self
    {
        $this->salary = $salary;
        return $this;
    }
    public function __toString(): string
    {
        return <<<INFO
        Name: {$this->name}
        Surname: {$this->surname}
        Salary: {$this->salary}
        INFO;
    }
}
# Create a new instance of the Employee class, Tom Smith, with a salary of 100:
$employee = (new Employee())
    ->setName('Tom')
    ->setSurname('Smith')
    ->setSalary('100');
# Display the value of the Employee instance:
echo $employee;
# Display:
# Name: Tom
# Surname: Smith
# Salary: 100

Python

In Python, returning self in the instance method is one way to implement the fluent pattern.

It is however discouraged by the language’s creator, Guido van Rossum, and therefore considered unpythonic (not idiomatic) for operations that do not return new values. Van Rossum provides string processing operations as example where he sees the fluent pattern appropriate.

class Poem:
    def __init__(self, title: str) -> None:
        self.title = title
    def indent(self, spaces: int):
        """Indent the poem with the specified number of spaces."""
        self.title = " " * spaces + self.title
        return self
    def suffix(self, author: str):
        """Suffix the poem with the author name."""
        self.title = f"{self.title} - {author}"
        return self
>>> Poem("Road Not Travelled").indent(4).suffix("Robert Frost").title
'    Road Not Travelled - Robert Frost'

Swift

In Swift 3.0+ returning self in the functions is one way to implement the fluent pattern.

class Person {
    var firstname: String = ""
    var lastname: String = ""
    var favoriteQuote: String = ""
    @discardableResult
    func set(firstname: String) -> Self {
        self.firstname = firstname
        return self
    }
    @discardableResult
    func set(lastname: String) -> Self {
        self.lastname = lastname
        return self
    }
    @discardableResult
    func set(favoriteQuote: String) -> Self {
        self.favoriteQuote = favoriteQuote
        return self
    }
}
let person = Person()
    .set(firstname: "John")
    .set(lastname: "Doe")
    .set(favoriteQuote: "I like turtles")

Immutability

It's possible to create immutable fluent interfaces that utilise copy-on-write semantics. In this variation of the pattern, instead of modifying internal properties and returning a reference to the same object, the object is instead cloned, with properties changed on the cloned object, and that object returned.

The benefit of this approach is that the interface can be used to create configurations of objects that can fork off from a particular point; Allowing two or more objects to share a certain amount of state, and be used further without interfering with each other.

JavaScript example

Using copy-on-write semantics, the JavaScript example from above becomes:

class Kitten {
  constructor() {
    this.name = 'Garfield';
    this.color = 'orange';
  }
  setName(name) {
    const copy = new Kitten();
    copy.color = this.color;
    copy.name = name;
    return copy;
  }
  setColor(color) {
    const copy = new Kitten();
    copy.name = this.name;
    copy.color = color;
    return copy;
  }
  // ...
}
// use it
const kitten1 = new Kitten()
  .setName('Salem');
const kitten2 = kitten1
  .setColor('black');
console.log(kitten1, kitten2);
// -> Kitten({ name: 'Salem', color: 'orange' }), Kitten({ name: 'Salem', color: 'black' })

Problems

Errors cannot be captured at compile time

In typed languages, using a constructor requiring all parameters will fail at compilation time while the fluent approach will only be able to generate runtime errors, missing all the type-safety checks of modern compilers. It also contradicts the "fail-fast" approach for error protection.

Debugging and error reporting

Single-line chained statements may be more difficult to debug as debuggers may not be able to set breakpoints within the chain. Stepping through a single-line statement in a debugger may also be less convenient.

java.nio.ByteBuffer.allocate(10).rewind().limit(100);

Another issue is that it may not be clear which of the method calls caused an exception, in particular if there are multiple calls to the same method. These issues can be overcome by breaking the statement into multiple lines which preserves readability while allowing the user to set breakpoints within the chain and to easily step through the code line by line:

java.nio.ByteBuffer
    .allocate(10)
    .rewind()
    .limit(100);

However, some debuggers always show the first line in the exception backtrace, although the exception has been thrown on any line.

Logging

Adding logging into the middle of a chain of fluent calls can be an issue. E.g., given:

ByteBuffer buffer = ByteBuffer.allocate(10).rewind().limit(100);

To log the state of buffer after the rewind() method call, it is necessary to break the fluent calls:

ByteBuffer buffer = ByteBuffer.allocate(10).rewind();
log.debug("First byte after rewind is " + buffer.get(0));
buffer.limit(100);

This can be worked around in languages that support extension methods by defining a new extension to wrap the desired logging functionality, for example in C# (using the same Java ByteBuffer example as above):

static class ByteBufferExtensions
{
    public static ByteBuffer Log(this ByteBuffer buffer, Log log, Action<ByteBuffer> getMessage)
    {
        string message = getMessage(buffer);
        log.debug(message);
        return buffer;
    } 
}
// Usage:
ByteBuffer
    .Allocate(10)
    .Rewind()
    .Log( log, b => "First byte after rewind is " + b.Get(0) )
    .Limit(100);

Subclasses

Subclasses in strongly typed languages (C++, Java, C#, etc.) often have to override all methods from their superclass that participate in a fluent interface in order to change their return type. For example:

class A {
    public A doThis() { ... }
}
class B extends A{
    public B doThis() { super.doThis(); return this; } // Must change return type to B.
    public B doThat() { ... }
}
...
A a = new B().doThat().doThis(); // This would work even without overriding A.doThis().
B b = new B().doThis().doThat(); // This would fail if A.doThis() wasn't overridden.

Languages that are capable of expressing F-bound polymorphism can use it to avoid this difficulty. For example:

abstract class AbstractA<T extends AbstractA<T>> {
	@SuppressWarnings("unchecked")
	public T doThis() { ...; return (T)this; }
}	
class A extends AbstractA<A> {}
class B extends AbstractA<B> {
	public B doThat() { ...; return this; }
}
...
B b = new B().doThis().doThat(); // Works!
A a = new A().doThis();          // Also works.

Note that in order to be able to create instances of the parent class, we had to split it into two classes — AbstractA and A, the latter with no content (it would only contain constructors if those were needed). The approach can easily be extended if we want to have sub-subclasses (etc.) too:

abstract class AbstractB<T extends AbstractB<T>> extends AbstractA<T> {
	@SuppressWarnings("unchecked")
	public T doThat() { ...; return (T)this; }
}
class B extends AbstractB<B> {}
abstract class AbstractC<T extends AbstractC<T>> extends AbstractB<T> {
	@SuppressWarnings("unchecked")
	public T foo() { ...; return (T)this; }
}
class C extends AbstractC<C> {}
...
C c = new C().doThis().doThat().foo(); // Works!
B b = new B().doThis().doThat();       // Still works.

In a dependently typed language, e.g. Scala, methods can also be explicitly defined as always returning this and thus can be defined only once for subclasses to take advantage of the fluent interface:

class A {
    def doThis(): this.type = { ... } // returns this, and always this.
}
class B extends A{
    // No override needed!
    def doThat(): this.type = { ... }
}
...
val a: A = new B().doThat().doThis(); // Chaining works in both directions.
val b: B = new B().doThis().doThat(); // And, both method chains result in a B!

See also

References

  1. ^ Martin Fowler, "FluentInterface", 20 December 2005
  2. "Interface Pack200.Packer". Oracle. Retrieved 13 November 2019.
  3. Rossum, Guido van (October 17, 2003). "[Python-Dev] sort() return value". Retrieved 2022-02-01.

External links

Software design patterns
Gang of Four
patterns
Creational
Structural
Behavioral
Concurrency
patterns
Architectural
patterns
Other
patterns
Books
People
Communities
See also
Category: