mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-14 02:16:06 +00:00
docs: improve fluent interface
This commit is contained in:
1 parent
6d385349cb
commit
ab1a30ba69
3 files changed
+85
-56
No files matched your search
+83
-54
@@ -1,15 +1,23 @@
|
|||||||
---
|
---
|
||||||
title: Fluent Interface
|
title: Fluent Interface
|
||||||
category: Functional
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Reactive
|
- API design
|
||||||
|
- Code simplification
|
||||||
|
- Decoupling
|
||||||
|
- Object composition
|
||||||
|
- Reactive
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
|
||||||
|
* Fluent API
|
||||||
|
* Method Chaining
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
|
|
||||||
A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific
|
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.
|
||||||
language. Using this pattern results in code that can be read nearly as human language.
|
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
@@ -24,8 +32,7 @@ A fluent interface can be implemented using any of
|
|||||||
|
|
||||||
Real world example
|
Real world example
|
||||||
|
|
||||||
> We need to select numbers based on different criteria from the list. It's a great chance to
|
> 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.
|
||||||
> utilize fluent interface pattern to provide readable easy-to-use developer experience.
|
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
@@ -33,9 +40,7 @@ In plain words
|
|||||||
|
|
||||||
Wikipedia says
|
Wikipedia says
|
||||||
|
|
||||||
> In software engineering, a fluent interface is an object-oriented API whose design relies
|
> 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).
|
||||||
> extensively on method chaining. Its goal is to increase code legibility by creating a
|
|
||||||
> domain-specific language (DSL).
|
|
||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
@@ -70,7 +75,7 @@ The `SimpleFluentIterable` evaluates eagerly and would be too costly for real wo
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class SimpleFluentIterable<E> implements FluentIterable<E> {
|
public class SimpleFluentIterable<E> implements FluentIterable<E> {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -78,64 +83,64 @@ The `LazyFluentIterable` is evaluated on termination.
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class LazyFluentIterable<E> implements FluentIterable<E> {
|
public class LazyFluentIterable<E> implements FluentIterable<E> {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Their usage is demonstrated with a simple number list that is filtered, transformed and collected. The
|
Their usage is demonstrated with a simple number list that is filtered, transformed and collected. The
|
||||||
result is printed afterwards.
|
result is printed afterward.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var integerList = List.of(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68);
|
var integerList = List.of(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68);
|
||||||
|
|
||||||
prettyPrint("The initial list contains: ", integerList);
|
prettyPrint("The initial list contains: ", integerList);
|
||||||
|
|
||||||
var firstFiveNegatives = SimpleFluentIterable
|
var firstFiveNegatives = SimpleFluentIterable
|
||||||
.fromCopyOf(integerList)
|
.fromCopyOf(integerList)
|
||||||
.filter(negatives())
|
.filter(negatives())
|
||||||
.first(3)
|
.first(3)
|
||||||
.asList();
|
.asList();
|
||||||
prettyPrint("The first three negative values are: ", firstFiveNegatives);
|
prettyPrint("The first three negative values are: ", firstFiveNegatives);
|
||||||
|
|
||||||
|
|
||||||
var lastTwoPositives = SimpleFluentIterable
|
var lastTwoPositives = SimpleFluentIterable
|
||||||
.fromCopyOf(integerList)
|
.fromCopyOf(integerList)
|
||||||
.filter(positives())
|
.filter(positives())
|
||||||
.last(2)
|
.last(2)
|
||||||
.asList();
|
.asList();
|
||||||
prettyPrint("The last two positive values are: ", lastTwoPositives);
|
prettyPrint("The last two positive values are: ", lastTwoPositives);
|
||||||
|
|
||||||
SimpleFluentIterable
|
SimpleFluentIterable
|
||||||
.fromCopyOf(integerList)
|
.fromCopyOf(integerList)
|
||||||
.filter(number -> number % 2 == 0)
|
.filter(number -> number % 2 == 0)
|
||||||
.first()
|
.first()
|
||||||
.ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
|
.ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
|
||||||
|
|
||||||
|
|
||||||
var transformedList = SimpleFluentIterable
|
var transformedList = SimpleFluentIterable
|
||||||
.fromCopyOf(integerList)
|
.fromCopyOf(integerList)
|
||||||
.filter(negatives())
|
.filter(negatives())
|
||||||
.map(transformToString())
|
.map(transformToString())
|
||||||
.asList();
|
.asList();
|
||||||
prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
|
prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
|
||||||
|
|
||||||
|
|
||||||
var lastTwoOfFirstFourStringMapped = LazyFluentIterable
|
var lastTwoOfFirstFourStringMapped = LazyFluentIterable
|
||||||
.from(integerList)
|
.from(integerList)
|
||||||
.filter(positives())
|
.filter(positives())
|
||||||
.first(4)
|
.first(4)
|
||||||
.last(2)
|
.last(2)
|
||||||
.map(number -> "String[" + valueOf(number) + "]")
|
.map(number -> "String[" + valueOf(number) + "]")
|
||||||
.asList();
|
.asList();
|
||||||
prettyPrint("The lazy list contains the last two of the first four positive numbers "
|
prettyPrint("The lazy list contains the last two of the first four positive numbers "
|
||||||
+ "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
|
+ "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
|
||||||
|
|
||||||
LazyFluentIterable
|
LazyFluentIterable
|
||||||
.from(integerList)
|
.from(integerList)
|
||||||
.filter(negatives())
|
.filter(negatives())
|
||||||
.first(2)
|
.first(2)
|
||||||
.last()
|
.last()
|
||||||
.ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
|
.ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
|
||||||
```
|
```
|
||||||
|
|
||||||
Program output:
|
Program output:
|
||||||
@@ -158,8 +163,9 @@ Last amongst first two negatives: -22
|
|||||||
|
|
||||||
Use the Fluent Interface pattern when
|
Use the Fluent Interface pattern when
|
||||||
|
|
||||||
* You provide an API that would benefit from a DSL-like usage.
|
* Designing APIs that are heavily used and where readability of client code is of high importance.
|
||||||
* You have objects that are difficult to configure or use.
|
* Building complex objects step-by-step, and there is a need to make the code more intuitive and less error-prone.
|
||||||
|
* Enhancing code clarity and reducing the boilerplate code, especially in configurations and object-building scenarios.
|
||||||
|
|
||||||
## Known uses
|
## Known uses
|
||||||
|
|
||||||
@@ -168,6 +174,26 @@ Use the Fluent Interface pattern when
|
|||||||
* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)
|
* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)
|
||||||
* [Mockito](http://mockito.org/)
|
* [Mockito](http://mockito.org/)
|
||||||
* [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial)
|
* [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial)
|
||||||
|
* Builders in libraries like Apache Camel for integration workflows.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
* Improved code readability and maintainability.
|
||||||
|
* Encourages building immutable objects since methods typically return new instances.
|
||||||
|
* Reduces the need for variables as the context is maintained in the chain.
|
||||||
|
|
||||||
|
Trade-offs:
|
||||||
|
|
||||||
|
* Can lead to less intuitive code for those unfamiliar with the pattern.
|
||||||
|
* Debugging can be challenging due to the chaining of method calls.
|
||||||
|
* Overuse can lead to complex and hard-to-maintain code structures.
|
||||||
|
|
||||||
|
## Related Patterns
|
||||||
|
|
||||||
|
* [Builder](https://java-design-patterns.com/patterns/builder/): Often implemented using a Fluent Interface to construct objects step-by-step. The Builder Pattern focuses on constructing complex objects, while Fluent Interface emphasizes the method chaining mechanism.
|
||||||
|
* [Chain of Responsibility](https://java-design-patterns.com/patterns/chain-of-responsibility/): Fluent Interfaces can be seen as a specific utilization of the Chain of Responsibility, where each method in the chain handles a part of the task and then delegates to the next method.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
@@ -175,3 +201,6 @@ Use the Fluent Interface pattern when
|
|||||||
* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/)
|
* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/)
|
||||||
* [Internal DSL](http://www.infoq.com/articles/internal-dsls-java)
|
* [Internal DSL](http://www.infoq.com/articles/internal-dsls-java)
|
||||||
* [Domain Specific Languages](https://www.amazon.com/gp/product/0321712943/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=0321712943&linkId=ad8351d6f5be7d8b7ecdb650731f85df)
|
* [Domain Specific Languages](https://www.amazon.com/gp/product/0321712943/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=0321712943&linkId=ad8351d6f5be7d8b7ecdb650731f85df)
|
||||||
|
* [Effective Java](https://amzn.to/4d4azvL)
|
||||||
|
* [Java Design Pattern Essentials](https://amzn.to/44bs6hG)
|
||||||
|
* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3UrXkh2)
|
||||||
@@ -42,7 +42,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world
|
* {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world
|
||||||
* applications. The {@link LazyFluentIterable} is evaluated on termination. Their usage is
|
* applications. The {@link LazyFluentIterable} is evaluated on termination. Their usage is
|
||||||
* demonstrated with a simple number list that is filtered, transformed and collected. The result is
|
* demonstrated with a simple number list that is filtered, transformed and collected. The result is
|
||||||
* printed afterwards.
|
* printed afterward.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|||||||
+1
-1
@@ -191,7 +191,7 @@ public abstract class FluentIterableTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSpliterator() throws Exception {
|
void testSpliterator() {
|
||||||
final var integers = List.of(1, 2, 3);
|
final var integers = List.of(1, 2, 3);
|
||||||
final var split = createFluentIterable(integers).spliterator();
|
final var split = createFluentIterable(integers).spliterator();
|
||||||
assertNotNull(split);
|
assertNotNull(split);
|
||||||
|
|||||||
Reference in new issue
Block a user