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.
The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those interfaces tend to mimic domain specific languages, so they can nearly be read as human languages.
A fluent interface can be implemented using any of
Real world example
We need to select numbers based on different criteria from the list. It’s a great chance to utilize fluent interface pattern to provide readable easy-to-use developer experience.
In plain words
Fluent Interface pattern provides easily readable flowing interface to code.
Wikipedia says
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).
Programmatic Example
In this example two implementations of a FluentIterable
interface are given.
1public interface FluentIterable<E> extends Iterable<E> {
2
3 FluentIterable<E> filter(Predicate<? super E> predicate);
4
5 Optional<E> first();
6
7 FluentIterable<E> first(int count);
8
9 Optional<E> last();
10
11 FluentIterable<E> last(int count);
12
13 <T> FluentIterable<T> map(Function<? super E, T> function);
14
15 List<E> asList();
16
17 static <E> List<E> copyToList(Iterable<E> iterable) {
18 var copy = new ArrayList<E>();
19 iterable.forEach(copy::add);
20 return copy;
21 }
22}
The SimpleFluentIterable
evaluates eagerly and would be too costly for real world applications.
1public class SimpleFluentIterable<E> implements FluentIterable<E> {
2 ...
3}
The LazyFluentIterable
is evaluated on termination.
1public class LazyFluentIterable<E> implements FluentIterable<E> {
2 ...
3}
Their usage is demonstrated with a simple number list that is filtered, transformed and collected. The result is printed afterwards.
1 var integerList = List.of(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68);
2
3 prettyPrint("The initial list contains: ", integerList);
4
5 var firstFiveNegatives = SimpleFluentIterable
6 .fromCopyOf(integerList)
7 .filter(negatives())
8 .first(3)
9 .asList();
10 prettyPrint("The first three negative values are: ", firstFiveNegatives);
11
12
13 var lastTwoPositives = SimpleFluentIterable
14 .fromCopyOf(integerList)
15 .filter(positives())
16 .last(2)
17 .asList();
18 prettyPrint("The last two positive values are: ", lastTwoPositives);
19
20 SimpleFluentIterable
21 .fromCopyOf(integerList)
22 .filter(number -> number % 2 == 0)
23 .first()
24 .ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
25
26
27 var transformedList = SimpleFluentIterable
28 .fromCopyOf(integerList)
29 .filter(negatives())
30 .map(transformToString())
31 .asList();
32 prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
33
34
35 var lastTwoOfFirstFourStringMapped = LazyFluentIterable
36 .from(integerList)
37 .filter(positives())
38 .first(4)
39 .last(2)
40 .map(number -> "String[" + valueOf(number) + "]")
41 .asList();
42 prettyPrint("The lazy list contains the last two of the first four positive numbers "
43 + "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
44
45 LazyFluentIterable
46 .from(integerList)
47 .filter(negatives())
48 .first(2)
49 .last()
50 .ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
Program output:
1The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68.
2The first three negative values are: -61, -22, -87.
3The last two positive values are: 23, 2.
4The first even number is: 14
5A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68].
6The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6].
7Last amongst first two negatives: -22
Use the Fluent Interface pattern when