Skip to content

Commit 0148cd7

Browse files
committed
Merge pull request iluwatar#225 from hannespernpeintner/fluentinterface
iluwatar#184 Fluent Interface pattern
2 parents 9eb64bc + fb13ddc commit 0148cd7

File tree

12 files changed

+866
-1
lines changed

12 files changed

+866
-1
lines changed

faq.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,7 @@ As for performance and scalability, pools can become bottlenecks, if all the
6161
pooled objects are in use and more clients need them, threads will become
6262
blocked waiting for available object from the pool. This is not the case with
6363
Flyweight.
64+
65+
### Q7: What are the differences between FluentInterface and Builder patterns? {#Q7}
66+
67+
Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change.
57.8 KB
Loading
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
3+
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
4+
<class id="1" language="java" name="com.iluwatar.fluentinterface.App" project="fluentinterface"
5+
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java" binary="false" corner="BOTTOM_RIGHT">
6+
<position height="-1" width="-1" x="289" y="-8"/>
7+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
8+
sort-features="false" accessors="true" visibility="true">
9+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
10+
<operations public="true" package="true" protected="true" private="true" static="true"/>
11+
</display>
12+
</class>
13+
<class id="2" language="java" name="com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable"
14+
project="fluentinterface"
15+
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java"
16+
binary="false" corner="BOTTOM_RIGHT">
17+
<position height="-1" width="-1" x="450" y="430"/>
18+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
19+
sort-features="false" accessors="true" visibility="true">
20+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
21+
<operations public="true" package="true" protected="true" private="true" static="true"/>
22+
</display>
23+
</class>
24+
<class id="3" language="java" name="com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable"
25+
project="fluentinterface"
26+
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java"
27+
binary="false" corner="BOTTOM_RIGHT">
28+
<position height="-1" width="-1" x="860" y="391"/>
29+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
30+
sort-features="false" accessors="true" visibility="true">
31+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
32+
<operations public="true" package="true" protected="true" private="true" static="true"/>
33+
</display>
34+
</class>
35+
<interface id="4" language="java" name="com.iluwatar.fluentinterface.fluentiterable.FluentIterable"
36+
project="fluentinterface"
37+
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java" binary="false"
38+
corner="BOTTOM_RIGHT">
39+
<position height="-1" width="-1" x="794" y="55"/>
40+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
41+
sort-features="false" accessors="true" visibility="true">
42+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
43+
<operations public="true" package="true" protected="true" private="true" static="true"/>
44+
</display>
45+
</interface>
46+
<class id="5" language="java" name="com.iluwatar.fluentinterface.fluentiterable.lazy.DecoratingIterator"
47+
project="fluentinterface"
48+
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java"
49+
binary="false" corner="BOTTOM_RIGHT">
50+
<position height="-1" width="-1" x="1245" y="391"/>
51+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
52+
sort-features="false" accessors="true" visibility="true">
53+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
54+
<operations public="true" package="true" protected="true" private="true" static="true"/>
55+
</display>
56+
</class>
57+
<interface id="6" language="java" name="java.lang.Iterable" project="fluentinterface"
58+
file="C:/Program Files/Java/jdk1.8.0/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
59+
<position height="-1" width="-1" x="793" y="-163"/>
60+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
61+
sort-features="false" accessors="true" visibility="true">
62+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
63+
<operations public="true" package="true" protected="true" private="true" static="true"/>
64+
</display>
65+
</interface>
66+
<dependency id="7">
67+
<end type="SOURCE" refId="1"/>
68+
<end type="TARGET" refId="3"/>
69+
</dependency>
70+
<dependency id="8">
71+
<end type="SOURCE" refId="1"/>
72+
<end type="TARGET" refId="2"/>
73+
</dependency>
74+
<generalization id="9">
75+
<end type="SOURCE" refId="4"/>
76+
<end type="TARGET" refId="6"/>
77+
</generalization>
78+
<realization id="10">
79+
<end type="SOURCE" refId="3"/>
80+
<end type="TARGET" refId="4"/>
81+
</realization>
82+
<dependency id="11">
83+
<end type="SOURCE" refId="1"/>
84+
<end type="TARGET" refId="4"/>
85+
</dependency>
86+
<realization id="12">
87+
<end type="SOURCE" refId="2"/>
88+
<end type="TARGET" refId="4"/>
89+
</realization>
90+
<dependency id="13">
91+
<end type="SOURCE" refId="3"/>
92+
<end type="TARGET" refId="5"/>
93+
</dependency>
94+
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
95+
sort-features="false" accessors="true" visibility="true">
96+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
97+
<operations public="true" package="true" protected="true" private="true" static="true"/>
98+
</classifier-display>
99+
<association-display labels="true" multiplicity="true"/>
100+
</class-diagram>

fluentinterface/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
layout: pattern
3+
title: Fluent Interface
4+
folder: fluentinterface
5+
permalink: /patterns/fluentinterface/
6+
categories: Architectural
7+
tags: Java
8+
---
9+
10+
**Intent:** 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.
11+
12+
![Fluent Interface](./etc/fluentinterface.png "Fluent Interface")
13+
14+
**Applicability:** Use the Fluent Interface pattern when
15+
16+
* you provide an API that would benefit from a DSL-like usage
17+
* you have objects that are difficult to configure or use
18+
19+
**Real world examples:**
20+
21+
* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html)
22+
* [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained)
23+
* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)
24+
25+
**Credits**
26+
27+
* [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html)
28+
* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/)

fluentinterface/pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>java-design-patterns</artifactId>
7+
<groupId>com.iluwatar</groupId>
8+
<version>1.6.0</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>fluentinterface</artifactId>
13+
<dependencies>
14+
<dependency>
15+
<groupId>junit</groupId>
16+
<artifactId>junit</artifactId>
17+
<scope>test</scope>
18+
</dependency>
19+
</dependencies>
20+
</project>
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.iluwatar.fluentinterface;
2+
3+
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
4+
import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable;
5+
import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable;
6+
7+
import java.util.*;
8+
import java.util.function.Function;
9+
import java.util.function.Predicate;
10+
11+
import static java.lang.String.valueOf;
12+
13+
/**
14+
* Fluent interface pattern is useful when you want to provide an easy readable, flowing API. Those
15+
* interfaces tend to mimic domain specific languages, so they can nearly be read as human
16+
* languages.
17+
* <p>
18+
* In this example two implementations of a {@link FluentIterable} interface are given. The
19+
* SimpleFluentIterable evaluates eagerly and would be too costly for real world applications. The
20+
* LazyFluentIterable is evaluated on termination. Their usage is demonstrated with a simple number
21+
* list that is filtered, transformed and collected. The result is printed afterwards.
22+
* <p>
23+
*/
24+
public class App {
25+
26+
public static void main(String[] args) {
27+
28+
List<Integer> integerList = new ArrayList<Integer>() {
29+
{
30+
add(1);
31+
add(-61);
32+
add(14);
33+
add(-22);
34+
add(18);
35+
add(-87);
36+
add(6);
37+
add(64);
38+
add(-82);
39+
add(26);
40+
add(-98);
41+
add(97);
42+
add(45);
43+
add(23);
44+
add(2);
45+
add(-68);
46+
add(45);
47+
}
48+
};
49+
prettyPrint("The initial list contains: ", integerList);
50+
51+
List<Integer> firstFiveNegatives =
52+
SimpleFluentIterable.fromCopyOf(integerList).filter(negatives()).first(3).asList();
53+
prettyPrint("The first three negative values are: ", firstFiveNegatives);
54+
55+
56+
List<Integer> lastTwoPositives =
57+
SimpleFluentIterable.fromCopyOf(integerList).filter(positives()).last(2).asList();
58+
prettyPrint("The last two positive values are: ", lastTwoPositives);
59+
60+
SimpleFluentIterable
61+
.fromCopyOf(integerList)
62+
.filter(number -> number % 2 == 0)
63+
.first()
64+
.ifPresent(
65+
evenNumber -> System.out.println(String.format("The first even number is: %d",
66+
evenNumber)));
67+
68+
69+
List<String> transformedList =
70+
SimpleFluentIterable.fromCopyOf(integerList).filter(negatives()).map(transformToString())
71+
.asList();
72+
prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
73+
74+
75+
List<String> lastTwoOfFirstFourStringMapped =
76+
LazyFluentIterable.from(integerList).filter(positives()).first(4).last(2)
77+
.map(number -> "String[" + String.valueOf(number) + "]").asList();
78+
prettyPrint(
79+
"The lazy list contains the last two of the first four positive numbers mapped to Strings: ",
80+
lastTwoOfFirstFourStringMapped);
81+
82+
LazyFluentIterable
83+
.from(integerList)
84+
.filter(negatives())
85+
.first(2)
86+
.last()
87+
.ifPresent(
88+
lastOfFirstTwo -> System.out.println(String.format(
89+
"The last of the first two negatives is: %d", lastOfFirstTwo)));
90+
}
91+
92+
private static Function<Integer, String> transformToString() {
93+
return integer -> "String[" + valueOf(integer) + "]";
94+
}
95+
96+
private static Predicate<? super Integer> negatives() {
97+
return integer -> (integer < 0);
98+
}
99+
100+
private static Predicate<? super Integer> positives() {
101+
return integer -> (integer > 0);
102+
}
103+
104+
private static <TYPE> void prettyPrint(String prefix, Iterable<TYPE> iterable) {
105+
prettyPrint(", ", prefix, ".", iterable);
106+
}
107+
108+
private static <TYPE> void prettyPrint(String prefix, String suffix, Iterable<TYPE> iterable) {
109+
prettyPrint(", ", prefix, suffix, iterable);
110+
}
111+
112+
private static <TYPE> void prettyPrint(String delimiter, String prefix, String suffix,
113+
Iterable<TYPE> iterable) {
114+
StringJoiner joiner = new StringJoiner(delimiter, prefix, ".");
115+
Iterator<TYPE> iterator = iterable.iterator();
116+
while (iterator.hasNext()) {
117+
joiner.add(iterator.next().toString());
118+
}
119+
120+
System.out.println(joiner);
121+
}
122+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.iluwatar.fluentinterface.fluentiterable;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
import java.util.Optional;
7+
import java.util.function.Function;
8+
import java.util.function.Predicate;
9+
10+
/**
11+
* The FluentIterable is a more convenient implementation of the common iterable interface based on
12+
* the fluent interface design pattern. This interface defines common operations, but doesn't aim to
13+
* be complete. It was inspired by Guava's com.google.common.collect.FluentIterable.
14+
*
15+
* @param <TYPE> is the class of objects the iterable contains
16+
*/
17+
public interface FluentIterable<TYPE> extends Iterable<TYPE> {
18+
19+
/**
20+
* Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy
21+
* the predicate.
22+
*
23+
* @param predicate the condition to test with for the filtering. If the test is negative, the
24+
* tested object is removed by the iterator.
25+
* @return a filtered FluentIterable
26+
*/
27+
FluentIterable<TYPE> filter(Predicate<? super TYPE> predicate);
28+
29+
/**
30+
* Returns an Optional containing the first element of this iterable if present, else returns
31+
* Optional.empty().
32+
*
33+
* @return the first element after the iteration is evaluated
34+
*/
35+
Optional<TYPE> first();
36+
37+
/**
38+
* Evaluates the iteration and leaves only the count first elements.
39+
*
40+
* @return the first count elements as an Iterable
41+
*/
42+
FluentIterable<TYPE> first(int count);
43+
44+
/**
45+
* Evaluates the iteration and returns the last element. This is a terminating operation.
46+
*
47+
* @return the last element after the iteration is evaluated
48+
*/
49+
Optional<TYPE> last();
50+
51+
/**
52+
* Evaluates the iteration and leaves only the count last elements.
53+
*
54+
* @return the last counts elements as an Iterable
55+
*/
56+
FluentIterable<TYPE> last(int count);
57+
58+
/**
59+
* Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE.
60+
*
61+
* @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE
62+
* @param <NEW_TYPE> the target type of the transformation
63+
* @return a new FluentIterable of the new type
64+
*/
65+
<NEW_TYPE> FluentIterable<NEW_TYPE> map(Function<? super TYPE, NEW_TYPE> function);
66+
67+
/**
68+
* Returns the contents of this Iterable as a List.
69+
*
70+
* @return a List representation of this Iterable
71+
*/
72+
List<TYPE> asList();
73+
74+
/**
75+
* Utility method that iterates over iterable and adds the contents to a list.
76+
*
77+
* @param iterable the iterable to collect
78+
* @param <TYPE> the type of the objects to iterate
79+
* @return a list with all objects of the given iterator
80+
*/
81+
static <TYPE> List<TYPE> copyToList(Iterable<TYPE> iterable) {
82+
ArrayList<TYPE> copy = new ArrayList<>();
83+
Iterator<TYPE> iterator = iterable.iterator();
84+
while (iterator.hasNext()) {
85+
copy.add(iterator.next());
86+
}
87+
return copy;
88+
}
89+
}

0 commit comments

Comments
 (0)