mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-17 08:59:12 +00:00
refactor: rename fluent interface module
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
---
|
||||
title: Fluent Interface
|
||||
category: Behavioral
|
||||
language: en
|
||||
tag:
|
||||
- API design
|
||||
- Code simplification
|
||||
- Decoupling
|
||||
- Object composition
|
||||
- Reactive
|
||||
---
|
||||
|
||||
## Also known as
|
||||
|
||||
* Fluent API
|
||||
* Method Chaining
|
||||
|
||||
## Intent
|
||||
|
||||
To provide an easily readable, flowing API by chaining method calls.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real-world example
|
||||
|
||||
> Imagine you are at a coffee shop and you want to customize your coffee order. Instead of telling the barista everything at once, you specify each customization step-by-step in a way that flows naturally. For instance, you might say, "I'd like a large coffee, add two shots of espresso, no sugar, and top it with almond milk." This approach is similar to the Fluent Interface design pattern, where you chain together method calls to configure an object in a readable and intuitive manner. Just as you specify each part of your coffee order sequentially, a Fluent Interface allows you to chain method calls to build and configure objects step-by-step in code.
|
||||
|
||||
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**
|
||||
|
||||
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 this example two implementations of a `FluentIterable` interface are given.
|
||||
|
||||
```java
|
||||
public interface FluentIterable<E> extends Iterable<E> {
|
||||
|
||||
FluentIterable<E> filter(Predicate<? super E> predicate);
|
||||
|
||||
Optional<E> first();
|
||||
|
||||
FluentIterable<E> first(int count);
|
||||
|
||||
Optional<E> last();
|
||||
|
||||
FluentIterable<E> last(int count);
|
||||
|
||||
<T> FluentIterable<T> map(Function<? super E, T> function);
|
||||
|
||||
List<E> asList();
|
||||
|
||||
static <E> List<E> copyToList(Iterable<E> iterable) {
|
||||
var copy = new ArrayList<E>();
|
||||
iterable.forEach(copy::add);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `SimpleFluentIterable` evaluates eagerly and would be too costly for real world applications.
|
||||
|
||||
```java
|
||||
public class SimpleFluentIterable<E> implements FluentIterable<E> {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The `LazyFluentIterable` is evaluated on termination.
|
||||
|
||||
```java
|
||||
public class LazyFluentIterable<E> implements FluentIterable<E> {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Their usage is demonstrated with a simple number list that is filtered, transformed and collected. The result is printed afterward.
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
|
||||
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);
|
||||
|
||||
var firstFiveNegatives = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(negatives())
|
||||
.first(3)
|
||||
.asList();
|
||||
prettyPrint("The first three negative values are: ", firstFiveNegatives);
|
||||
|
||||
|
||||
var lastTwoPositives = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(positives())
|
||||
.last(2)
|
||||
.asList();
|
||||
prettyPrint("The last two positive values are: ", lastTwoPositives);
|
||||
|
||||
SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(number -> number % 2 == 0)
|
||||
.first()
|
||||
.ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
|
||||
|
||||
|
||||
var transformedList = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(negatives())
|
||||
.map(transformToString())
|
||||
.asList();
|
||||
prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
|
||||
|
||||
|
||||
var lastTwoOfFirstFourStringMapped = LazyFluentIterable
|
||||
.from(integerList)
|
||||
.filter(positives())
|
||||
.first(4)
|
||||
.last(2)
|
||||
.map(number -> "String[" + number + "]")
|
||||
.asList();
|
||||
prettyPrint("The lazy list contains the last two of the first four positive numbers "
|
||||
+ "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
|
||||
|
||||
LazyFluentIterable
|
||||
.from(integerList)
|
||||
.filter(negatives())
|
||||
.first(2)
|
||||
.last()
|
||||
.ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
|
||||
}
|
||||
```
|
||||
|
||||
Program output:
|
||||
|
||||
```
|
||||
08:50:08.260 [main] INFO com.iluwatar.fluentinterface.app.App -- The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68.
|
||||
08:50:08.265 [main] INFO com.iluwatar.fluentinterface.app.App -- The first three negative values are: -61, -22, -87.
|
||||
08:50:08.265 [main] INFO com.iluwatar.fluentinterface.app.App -- The last two positive values are: 23, 2.
|
||||
08:50:08.266 [main] INFO com.iluwatar.fluentinterface.app.App -- The first even number is: 14
|
||||
08:50:08.267 [main] INFO com.iluwatar.fluentinterface.app.App -- A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68].
|
||||
08:50:08.270 [main] INFO com.iluwatar.fluentinterface.app.App -- The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6].
|
||||
08:50:08.270 [main] INFO com.iluwatar.fluentinterface.app.App -- Last amongst first two negatives: -22
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
|
||||
Use the Fluent Interface pattern when
|
||||
|
||||
* Designing APIs that are heavily used and where readability of client code is of high importance.
|
||||
* 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.
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [An Approach to Internal Domain-Specific Languages in Java (InfoQ)](http://www.infoq.com/articles/internal-dsls-java)
|
||||
|
||||
## Known uses
|
||||
|
||||
* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html)
|
||||
* [Google Guava FluentIterable](https://github.com/google/guava/wiki/FunctionalExplained)
|
||||
* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)
|
||||
* [Mockito](http://mockito.org/)
|
||||
* [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
|
||||
|
||||
* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3UrXkh2)
|
||||
* [Domain Specific Languages](https://amzn.to/3R1UYDA)
|
||||
* [Effective Java](https://amzn.to/4d4azvL)
|
||||
* [Java Design Pattern Essentials](https://amzn.to/44bs6hG)
|
||||
* [Fluent Interface (Martin Fowler)](http://www.martinfowler.com/bliki/FluentInterface.html)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
|
||||
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
|
||||
<class id="1" language="java" name="com.iluwatar.fluentinterface.app.App" project="fluentinterface"
|
||||
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java" binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="289" y="-8"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="false" protected="false" private="false" static="false"/>
|
||||
<operations public="true" package="true" protected="true" private="false" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="2" language="java" name="com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable"
|
||||
project="fluentinterface"
|
||||
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java"
|
||||
binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="450" y="430"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<class id="3" language="java" name="com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable"
|
||||
project="fluentinterface"
|
||||
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java"
|
||||
binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="860" y="391"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<interface id="4" language="java" name="com.iluwatar.fluentinterface.fluentiterable.FluentIterable"
|
||||
project="fluentinterface"
|
||||
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java" binary="false"
|
||||
corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="794" y="55"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<class id="5" language="java" name="com.iluwatar.fluentinterface.fluentiterable.lazy.DecoratingIterator"
|
||||
project="fluentinterface"
|
||||
file="/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java"
|
||||
binary="false" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="1245" y="391"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</class>
|
||||
<interface id="6" language="java" name="java.lang.Iterable" project="fluentinterface"
|
||||
file="C:/Program Files/Java/jdk1.8.0_25/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
|
||||
<position height="-1" width="-1" x="793" y="-163"/>
|
||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</display>
|
||||
</interface>
|
||||
<dependency id="7">
|
||||
<end type="SOURCE" refId="3"/>
|
||||
<end type="TARGET" refId="5"/>
|
||||
</dependency>
|
||||
<dependency id="8">
|
||||
<end type="SOURCE" refId="1"/>
|
||||
<end type="TARGET" refId="3"/>
|
||||
</dependency>
|
||||
<dependency id="9">
|
||||
<end type="SOURCE" refId="1"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</dependency>
|
||||
<realization id="10">
|
||||
<end type="SOURCE" refId="2"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</realization>
|
||||
<realization id="11">
|
||||
<end type="SOURCE" refId="3"/>
|
||||
<end type="TARGET" refId="4"/>
|
||||
</realization>
|
||||
<dependency id="12">
|
||||
<end type="SOURCE" refId="1"/>
|
||||
<end type="TARGET" refId="2"/>
|
||||
</dependency>
|
||||
<generalization id="13">
|
||||
<end type="SOURCE" refId="4"/>
|
||||
<end type="TARGET" refId="6"/>
|
||||
</generalization>
|
||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="true" signature="true"
|
||||
sort-features="false" accessors="true" visibility="true">
|
||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
||||
</classifier-display>
|
||||
<association-display labels="true" multiplicity="true"/>
|
||||
</class-diagram>
|
||||
@@ -0,0 +1,72 @@
|
||||
@startuml
|
||||
package com.iluwatar.fluentinterface.fluentiterable.simple {
|
||||
class SimpleFluentIterable<E> {
|
||||
- iterable : Iterable<E>
|
||||
# SimpleFluentIterable<E>(iterable : Iterable<E>)
|
||||
+ asList() : List<E>
|
||||
+ filter(predicate : Predicate<? super E>) : FluentIterable<E>
|
||||
+ first() : Optional<E>
|
||||
+ first(count : int) : FluentIterable<E>
|
||||
+ forEach(action : Consumer<? super E>)
|
||||
+ from(iterable : Iterable<E>) : FluentIterable<E> {static}
|
||||
+ fromCopyOf(iterable : Iterable<E>) : FluentIterable<E> {static}
|
||||
+ getRemainingElementsCount() : int
|
||||
+ iterator() : Iterator<E>
|
||||
+ last() : Optional<E>
|
||||
+ last(count : int) : FluentIterable<E>
|
||||
+ map(function : Function<? super E, T>) : FluentIterable<T>
|
||||
+ spliterator() : Spliterator<E>
|
||||
+ toList(iterator : Iterator<E>) : List<E> {static}
|
||||
}
|
||||
}
|
||||
package com.iluwatar.fluentinterface.app {
|
||||
class App {
|
||||
- LOGGER : Logger {static}
|
||||
+ App()
|
||||
+ main(args : String[]) {static}
|
||||
- negatives() : Predicate<? super Integer> {static}
|
||||
- positives() : Predicate<? super Integer> {static}
|
||||
- prettyPrint(delimiter : String, prefix : String, iterable : Iterable<E>) {static}
|
||||
- prettyPrint(prefix : String, iterable : Iterable<E>) {static}
|
||||
- transformToString() : Function<Integer, String> {static}
|
||||
}
|
||||
}
|
||||
package com.iluwatar.fluentinterface.fluentiterable.lazy {
|
||||
abstract class DecoratingIterator<E> {
|
||||
# fromIterator : Iterator<E>
|
||||
- next : E
|
||||
+ DecoratingIterator<E>(fromIterator : Iterator<E>)
|
||||
+ computeNext() : E {abstract}
|
||||
+ hasNext() : boolean
|
||||
+ next() : E
|
||||
}
|
||||
class LazyFluentIterable<E> {
|
||||
- iterable : Iterable<E>
|
||||
# LazyFluentIterable<E>()
|
||||
# LazyFluentIterable<E>(iterable : Iterable<E>)
|
||||
+ asList() : List<E>
|
||||
+ filter(predicate : Predicate<? super E>) : FluentIterable<E>
|
||||
+ first() : Optional<E>
|
||||
+ first(count : int) : FluentIterable<E>
|
||||
+ from(iterable : Iterable<E>) : FluentIterable<E> {static}
|
||||
+ iterator() : Iterator<E>
|
||||
+ last() : Optional<E>
|
||||
+ last(count : int) : FluentIterable<E>
|
||||
+ map(function : Function<? super E, T>) : FluentIterable<T>
|
||||
}
|
||||
}
|
||||
package com.iluwatar.fluentinterface.fluentiterable {
|
||||
interface FluentIterable<E> {
|
||||
+ asList() : List<E> {abstract}
|
||||
+ copyToList(iterable : Iterable<E>) : List<E> {static}
|
||||
+ filter(Predicate<? super E>) : FluentIterable<E> {abstract}
|
||||
+ first() : Optional<E> {abstract}
|
||||
+ first(int) : FluentIterable<E> {abstract}
|
||||
+ last() : Optional<E> {abstract}
|
||||
+ last(int) : FluentIterable<E> {abstract}
|
||||
+ map(Function<? super E, T>) : FluentIterable<T> {abstract}
|
||||
}
|
||||
}
|
||||
LazyFluentIterable ..|> FluentIterable
|
||||
SimpleFluentIterable ..|> FluentIterable
|
||||
@enduml
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
|
||||
The MIT License
|
||||
Copyright © 2014-2022 Ilkka Seppälä
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<version>1.26.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>fluent-interface</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.iluwatar.fluentinterface.app.App</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.app;
|
||||
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
|
||||
import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable;
|
||||
import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p>In this example two implementations of a {@link FluentIterable} interface are given. The
|
||||
* {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world
|
||||
* 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
|
||||
* printed afterward.
|
||||
*/
|
||||
@Slf4j
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* Program entry point.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
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);
|
||||
|
||||
var firstFiveNegatives = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(negatives())
|
||||
.first(3)
|
||||
.asList();
|
||||
prettyPrint("The first three negative values are: ", firstFiveNegatives);
|
||||
|
||||
|
||||
var lastTwoPositives = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(positives())
|
||||
.last(2)
|
||||
.asList();
|
||||
prettyPrint("The last two positive values are: ", lastTwoPositives);
|
||||
|
||||
SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(number -> number % 2 == 0)
|
||||
.first()
|
||||
.ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
|
||||
|
||||
|
||||
var transformedList = SimpleFluentIterable
|
||||
.fromCopyOf(integerList)
|
||||
.filter(negatives())
|
||||
.map(transformToString())
|
||||
.asList();
|
||||
prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
|
||||
|
||||
|
||||
var lastTwoOfFirstFourStringMapped = LazyFluentIterable
|
||||
.from(integerList)
|
||||
.filter(positives())
|
||||
.first(4)
|
||||
.last(2)
|
||||
.map(number -> "String[" + number + "]")
|
||||
.asList();
|
||||
prettyPrint("The lazy list contains the last two of the first four positive numbers "
|
||||
+ "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
|
||||
|
||||
LazyFluentIterable
|
||||
.from(integerList)
|
||||
.filter(negatives())
|
||||
.first(2)
|
||||
.last()
|
||||
.ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
|
||||
}
|
||||
|
||||
private static Function<Integer, String> transformToString() {
|
||||
return integer -> "String[" + integer + "]";
|
||||
}
|
||||
|
||||
private static Predicate<? super Integer> negatives() {
|
||||
return integer -> integer < 0;
|
||||
}
|
||||
|
||||
private static Predicate<? super Integer> positives() {
|
||||
return integer -> integer > 0;
|
||||
}
|
||||
|
||||
private static <E> void prettyPrint(String prefix, Iterable<E> iterable) {
|
||||
prettyPrint(", ", prefix, iterable);
|
||||
}
|
||||
|
||||
private static <E> void prettyPrint(
|
||||
String delimiter, String prefix,
|
||||
Iterable<E> iterable
|
||||
) {
|
||||
var joiner = new StringJoiner(delimiter, prefix, ".");
|
||||
iterable.forEach(e -> joiner.add(e.toString()));
|
||||
LOGGER.info(joiner.toString());
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The FluentIterable is a more convenient implementation of the common iterable interface based on
|
||||
* the fluent interface design pattern. This interface defines common operations, but doesn't aim to
|
||||
* be complete. It was inspired by Guava's com.google.common.collect.FluentIterable.
|
||||
*
|
||||
* @param <E> is the class of objects the iterable contains
|
||||
*/
|
||||
public interface FluentIterable<E> extends Iterable<E> {
|
||||
|
||||
/**
|
||||
* Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy
|
||||
* the predicate.
|
||||
*
|
||||
* @param predicate the condition to test with for the filtering. If the test is negative, the
|
||||
* tested object is removed by the iterator.
|
||||
* @return a filtered FluentIterable
|
||||
*/
|
||||
FluentIterable<E> filter(Predicate<? super E> predicate);
|
||||
|
||||
/**
|
||||
* Returns an Optional containing the first element of this iterable if present, else returns
|
||||
* Optional.empty().
|
||||
*
|
||||
* @return the first element after the iteration is evaluated
|
||||
*/
|
||||
Optional<E> first();
|
||||
|
||||
/**
|
||||
* Evaluates the iteration and leaves only the count first elements.
|
||||
*
|
||||
* @return the first count elements as an Iterable
|
||||
*/
|
||||
FluentIterable<E> first(int count);
|
||||
|
||||
/**
|
||||
* Evaluates the iteration and returns the last element. This is a terminating operation.
|
||||
*
|
||||
* @return the last element after the iteration is evaluated
|
||||
*/
|
||||
Optional<E> last();
|
||||
|
||||
/**
|
||||
* Evaluates the iteration and leaves only the count last elements.
|
||||
*
|
||||
* @return the last counts elements as an Iterable
|
||||
*/
|
||||
FluentIterable<E> last(int count);
|
||||
|
||||
/**
|
||||
* Transforms this FluentIterable into a new one containing objects of the type T.
|
||||
*
|
||||
* @param function a function that transforms an instance of E into an instance of T
|
||||
* @param <T> the target type of the transformation
|
||||
* @return a new FluentIterable of the new type
|
||||
*/
|
||||
<T> FluentIterable<T> map(Function<? super E, T> function);
|
||||
|
||||
/**
|
||||
* Returns the contents of this Iterable as a List.
|
||||
*
|
||||
* @return a List representation of this Iterable
|
||||
*/
|
||||
List<E> asList();
|
||||
|
||||
/**
|
||||
* Utility method that iterates over iterable and adds the contents to a list.
|
||||
*
|
||||
* @param iterable the iterable to collect
|
||||
* @param <E> the type of the objects to iterate
|
||||
* @return a list with all objects of the given iterator
|
||||
*/
|
||||
static <E> List<E> copyToList(Iterable<E> iterable) {
|
||||
var copy = new ArrayList<E>();
|
||||
iterable.forEach(copy::add);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable.lazy;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class is used to realize LazyFluentIterables. It decorates a given iterator. Does not
|
||||
* support consecutive hasNext() calls.
|
||||
*
|
||||
* @param <E> Iterable Collection of Elements of Type E
|
||||
*/
|
||||
public abstract class DecoratingIterator<E> implements Iterator<E> {
|
||||
|
||||
protected final Iterator<E> fromIterator;
|
||||
|
||||
private E next;
|
||||
|
||||
/**
|
||||
* Creates an iterator that decorates the given iterator.
|
||||
*/
|
||||
public DecoratingIterator(Iterator<E> fromIterator) {
|
||||
this.fromIterator = fromIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Precomputes and saves the next element of the Iterable. null is considered as end of data.
|
||||
*
|
||||
* @return true if a next element is available
|
||||
*/
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
next = computeNext();
|
||||
return next != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element of the Iterable.
|
||||
*
|
||||
* @return the next element of the Iterable, or null if not present.
|
||||
*/
|
||||
@Override
|
||||
public final E next() {
|
||||
if (next == null) {
|
||||
return fromIterator.next();
|
||||
} else {
|
||||
final var result = next;
|
||||
next = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the next object of the Iterable. Can be implemented to realize custom behaviour for an
|
||||
* iteration process. null is considered as end of data.
|
||||
*
|
||||
* @return the next element of the Iterable.
|
||||
*/
|
||||
public abstract E computeNext();
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable.lazy;
|
||||
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* This is a lazy implementation of the FluentIterable interface. It evaluates all chained
|
||||
* operations when a terminating operation is applied.
|
||||
*
|
||||
* @param <E> the type of the objects the iteration is about
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class LazyFluentIterable<E> implements FluentIterable<E> {
|
||||
|
||||
private final Iterable<E> iterable;
|
||||
|
||||
/**
|
||||
* This constructor can be used to implement anonymous subclasses of the LazyFluentIterable.
|
||||
*/
|
||||
protected LazyFluentIterable() {
|
||||
iterable = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy
|
||||
* the predicate.
|
||||
*
|
||||
* @param predicate the condition to test with for the filtering. If the test is negative, the
|
||||
* tested object is removed by the iterator.
|
||||
* @return a new FluentIterable object that decorates the source iterable
|
||||
*/
|
||||
@Override
|
||||
public FluentIterable<E> filter(Predicate<? super E> predicate) {
|
||||
return new LazyFluentIterable<>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new DecoratingIterator<>(iterable.iterator()) {
|
||||
@Override
|
||||
public E computeNext() {
|
||||
while (fromIterator.hasNext()) {
|
||||
var candidate = fromIterator.next();
|
||||
if (predicate.test(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the iteration. Is a terminating operation.
|
||||
*
|
||||
* @return an Optional containing the first object of this Iterable
|
||||
*/
|
||||
@Override
|
||||
public Optional<E> first() {
|
||||
var resultIterator = first(1).iterator();
|
||||
return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the iteration.
|
||||
*
|
||||
* @param count defines the number of objects to return
|
||||
* @return the same FluentIterable with a collection decimated to a maximum of 'count' first
|
||||
* objects.
|
||||
*/
|
||||
@Override
|
||||
public FluentIterable<E> first(int count) {
|
||||
return new LazyFluentIterable<>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new DecoratingIterator<>(iterable.iterator()) {
|
||||
int currentIndex;
|
||||
|
||||
@Override
|
||||
public E computeNext() {
|
||||
if (currentIndex < count && fromIterator.hasNext()) {
|
||||
var candidate = fromIterator.next();
|
||||
currentIndex++;
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the iteration. Is a terminating operation.
|
||||
*
|
||||
* @return an Optional containing the last object of this Iterable
|
||||
*/
|
||||
@Override
|
||||
public Optional<E> last() {
|
||||
var resultIterator = last(1).iterator();
|
||||
return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the Iterable. Is a terminating operation. This operation is
|
||||
* memory intensive, because the contents of this Iterable are collected into a List, when the
|
||||
* next object is requested.
|
||||
*
|
||||
* @param count defines the number of objects to return
|
||||
* @return the same FluentIterable with a collection decimated to a maximum of 'count' last
|
||||
* objects
|
||||
*/
|
||||
@Override
|
||||
public FluentIterable<E> last(int count) {
|
||||
return new LazyFluentIterable<>() {
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new DecoratingIterator<>(iterable.iterator()) {
|
||||
private int stopIndex;
|
||||
private int totalElementsCount;
|
||||
private List<E> list;
|
||||
private int currentIndex;
|
||||
|
||||
@Override
|
||||
public E computeNext() {
|
||||
initialize();
|
||||
|
||||
while (currentIndex < stopIndex && fromIterator.hasNext()) {
|
||||
currentIndex++;
|
||||
fromIterator.next();
|
||||
}
|
||||
if (currentIndex >= stopIndex && fromIterator.hasNext()) {
|
||||
return fromIterator.next();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
iterable.forEach(list::add);
|
||||
totalElementsCount = list.size();
|
||||
stopIndex = totalElementsCount - count;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms this FluentIterable into a new one containing objects of the type T.
|
||||
*
|
||||
* @param function a function that transforms an instance of E into an instance of T
|
||||
* @param <T> the target type of the transformation
|
||||
* @return a new FluentIterable of the new type
|
||||
*/
|
||||
@Override
|
||||
public <T> FluentIterable<T> map(Function<? super E, T> function) {
|
||||
return new LazyFluentIterable<>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new DecoratingIterator<>(null) {
|
||||
final Iterator<E> oldTypeIterator = iterable.iterator();
|
||||
|
||||
@Override
|
||||
public T computeNext() {
|
||||
if (oldTypeIterator.hasNext()) {
|
||||
E candidate = oldTypeIterator.next();
|
||||
return function.apply(candidate);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all remaining objects of this iteration into a list.
|
||||
*
|
||||
* @return a list with all remaining objects of this iteration
|
||||
*/
|
||||
@Override
|
||||
public List<E> asList() {
|
||||
return FluentIterable.copyToList(iterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new DecoratingIterator<>(iterable.iterator()) {
|
||||
@Override
|
||||
public E computeNext() {
|
||||
return fromIterator.hasNext() ? fromIterator.next() : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructors FluentIterable from given iterable.
|
||||
*
|
||||
* @return a FluentIterable from a given iterable. Calls the LazyFluentIterable constructor.
|
||||
*/
|
||||
public static <E> FluentIterable<E> from(Iterable<E> iterable) {
|
||||
return new LazyFluentIterable<>(iterable);
|
||||
}
|
||||
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable.simple;
|
||||
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* This is a simple implementation of the FluentIterable interface. It evaluates all chained
|
||||
* operations eagerly. This implementation would be costly to be utilized in real applications.
|
||||
*
|
||||
* @param <E> the type of the objects the iteration is about
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleFluentIterable<E> implements FluentIterable<E> {
|
||||
|
||||
private final Iterable<E> iterable;
|
||||
|
||||
/**
|
||||
* Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy
|
||||
* the predicate.
|
||||
*
|
||||
* @param predicate the condition to test with for the filtering. If the test is negative, the
|
||||
* tested object is removed by the iterator.
|
||||
* @return the same FluentIterable with a filtered collection
|
||||
*/
|
||||
@Override
|
||||
public final FluentIterable<E> filter(Predicate<? super E> predicate) {
|
||||
var iterator = iterator();
|
||||
while (iterator.hasNext()) {
|
||||
var nextElement = iterator.next();
|
||||
if (!predicate.test(nextElement)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the Iterable. Is a terminating operation.
|
||||
*
|
||||
* @return an option of the first object of the Iterable
|
||||
*/
|
||||
@Override
|
||||
public final Optional<E> first() {
|
||||
var resultIterator = first(1).iterator();
|
||||
return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the Iterable. Is a terminating operation.
|
||||
*
|
||||
* @param count defines the number of objects to return
|
||||
* @return the same FluentIterable with a collection decimated to a maximum of 'count' first
|
||||
* objects.
|
||||
*/
|
||||
@Override
|
||||
public final FluentIterable<E> first(int count) {
|
||||
var iterator = iterator();
|
||||
var currentCount = 0;
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
if (currentCount >= count) {
|
||||
iterator.remove();
|
||||
}
|
||||
currentCount++;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the Iterable. Is a terminating operation.
|
||||
*
|
||||
* @return an option of the last object of the Iterable
|
||||
*/
|
||||
@Override
|
||||
public final Optional<E> last() {
|
||||
var list = last(1).asList();
|
||||
if (list.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(list.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to collect objects from the Iterable. Is a terminating operation.
|
||||
*
|
||||
* @param count defines the number of objects to return
|
||||
* @return the same FluentIterable with a collection decimated to a maximum of 'count' last
|
||||
* objects
|
||||
*/
|
||||
@Override
|
||||
public final FluentIterable<E> last(int count) {
|
||||
var remainingElementsCount = getRemainingElementsCount();
|
||||
var iterator = iterator();
|
||||
var currentIndex = 0;
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
if (currentIndex < remainingElementsCount - count) {
|
||||
iterator.remove();
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms this FluentIterable into a new one containing objects of the type T.
|
||||
*
|
||||
* @param function a function that transforms an instance of E into an instance of T
|
||||
* @param <T> the target type of the transformation
|
||||
* @return a new FluentIterable of the new type
|
||||
*/
|
||||
@Override
|
||||
public final <T> FluentIterable<T> map(Function<? super E, T> function) {
|
||||
var temporaryList = new ArrayList<T>();
|
||||
this.forEach(e -> temporaryList.add(function.apply(e)));
|
||||
return from(temporaryList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all remaining objects of this Iterable into a list.
|
||||
*
|
||||
* @return a list with all remaining objects of this Iterable
|
||||
*/
|
||||
@Override
|
||||
public List<E> asList() {
|
||||
return toList(iterable.iterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs FluentIterable from iterable.
|
||||
*
|
||||
* @return a FluentIterable from a given iterable. Calls the SimpleFluentIterable constructor.
|
||||
*/
|
||||
public static <E> FluentIterable<E> from(Iterable<E> iterable) {
|
||||
return new SimpleFluentIterable<>(iterable);
|
||||
}
|
||||
|
||||
public static <E> FluentIterable<E> fromCopyOf(Iterable<E> iterable) {
|
||||
var copy = FluentIterable.copyToList(iterable);
|
||||
return new SimpleFluentIterable<>(copy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return iterable.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super E> action) {
|
||||
iterable.forEach(action);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return iterable.spliterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the count of remaining objects of current iterable.
|
||||
*
|
||||
* @return the count of remaining objects of the current Iterable
|
||||
*/
|
||||
public final int getRemainingElementsCount() {
|
||||
var counter = 0;
|
||||
for (var ignored : this) {
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the remaining objects of the given iterator into a List.
|
||||
*
|
||||
* @return a new List with the remaining objects.
|
||||
*/
|
||||
public static <E> List<E> toList(Iterator<E> iterator) {
|
||||
var copy = new ArrayList<E>();
|
||||
iterator.forEachRemaining(copy::add);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.app;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Application Test Entry
|
||||
*/
|
||||
class AppTest {
|
||||
|
||||
@Test
|
||||
void shouldExecuteWithoutException() {
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* FluentIterableTest
|
||||
*
|
||||
*/
|
||||
public abstract class FluentIterableTest {
|
||||
|
||||
/**
|
||||
* Create a new {@link FluentIterable} from the given integers
|
||||
*
|
||||
* @param integers The integers
|
||||
* @return The new iterable, use for testing
|
||||
*/
|
||||
protected abstract FluentIterable<Integer> createFluentIterable(final Iterable<Integer> integers);
|
||||
|
||||
@Test
|
||||
void testFirst() {
|
||||
final var integers = List.of(1, 2, 3, 10, 9, 8);
|
||||
final var first = createFluentIterable(integers).first();
|
||||
assertNotNull(first);
|
||||
assertTrue(first.isPresent());
|
||||
assertEquals(integers.get(0), first.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFirstEmptyCollection() {
|
||||
final var integers = Collections.<Integer>emptyList();
|
||||
final var first = createFluentIterable(integers).first();
|
||||
assertNotNull(first);
|
||||
assertFalse(first.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFirstCount() {
|
||||
final var integers = List.of(1, 2, 3, 10, 9, 8);
|
||||
final var first4 = createFluentIterable(integers)
|
||||
.first(4)
|
||||
.asList();
|
||||
|
||||
assertNotNull(first4);
|
||||
assertEquals(4, first4.size());
|
||||
|
||||
assertEquals(integers.get(0), first4.get(0));
|
||||
assertEquals(integers.get(1), first4.get(1));
|
||||
assertEquals(integers.get(2), first4.get(2));
|
||||
assertEquals(integers.get(3), first4.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFirstCountLessItems() {
|
||||
final var integers = List.of(1, 2, 3);
|
||||
final var first4 = createFluentIterable(integers)
|
||||
.first(4)
|
||||
.asList();
|
||||
|
||||
assertNotNull(first4);
|
||||
assertEquals(3, first4.size());
|
||||
|
||||
assertEquals(integers.get(0), first4.get(0));
|
||||
assertEquals(integers.get(1), first4.get(1));
|
||||
assertEquals(integers.get(2), first4.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLast() {
|
||||
final var integers = List.of(1, 2, 3, 10, 9, 8);
|
||||
final var last = createFluentIterable(integers).last();
|
||||
assertNotNull(last);
|
||||
assertTrue(last.isPresent());
|
||||
assertEquals(integers.get(integers.size() - 1), last.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLastEmptyCollection() {
|
||||
final var integers = Collections.<Integer>emptyList();
|
||||
final var last = createFluentIterable(integers).last();
|
||||
assertNotNull(last);
|
||||
assertFalse(last.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLastCount() {
|
||||
final var integers = List.of(1, 2, 3, 10, 9, 8);
|
||||
final var last4 = createFluentIterable(integers)
|
||||
.last(4)
|
||||
.asList();
|
||||
|
||||
assertNotNull(last4);
|
||||
assertEquals(4, last4.size());
|
||||
assertEquals(Integer.valueOf(3), last4.get(0));
|
||||
assertEquals(Integer.valueOf(10), last4.get(1));
|
||||
assertEquals(Integer.valueOf(9), last4.get(2));
|
||||
assertEquals(Integer.valueOf(8), last4.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLastCountLessItems() {
|
||||
final var integers = List.of(1, 2, 3);
|
||||
final var last4 = createFluentIterable(integers)
|
||||
.last(4)
|
||||
.asList();
|
||||
|
||||
assertNotNull(last4);
|
||||
assertEquals(3, last4.size());
|
||||
|
||||
assertEquals(Integer.valueOf(1), last4.get(0));
|
||||
assertEquals(Integer.valueOf(2), last4.get(1));
|
||||
assertEquals(Integer.valueOf(3), last4.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilter() {
|
||||
final var integers = List.of(1, 2, 3, 10, 9, 8);
|
||||
final var evenItems = createFluentIterable(integers)
|
||||
.filter(i -> i % 2 == 0)
|
||||
.asList();
|
||||
|
||||
assertNotNull(evenItems);
|
||||
assertEquals(3, evenItems.size());
|
||||
assertEquals(Integer.valueOf(2), evenItems.get(0));
|
||||
assertEquals(Integer.valueOf(10), evenItems.get(1));
|
||||
assertEquals(Integer.valueOf(8), evenItems.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMap() {
|
||||
final var integers = List.of(1, 2, 3);
|
||||
final var longs = createFluentIterable(integers)
|
||||
.map(Integer::longValue)
|
||||
.asList();
|
||||
|
||||
assertNotNull(longs);
|
||||
assertEquals(integers.size(), longs.size());
|
||||
assertEquals(Long.valueOf(1), longs.get(0));
|
||||
assertEquals(Long.valueOf(2), longs.get(1));
|
||||
assertEquals(Long.valueOf(3), longs.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testForEach() {
|
||||
final var integers = List.of(1, 2, 3);
|
||||
|
||||
final Consumer<Integer> consumer = mock(Consumer.class);
|
||||
createFluentIterable(integers).forEach(consumer);
|
||||
|
||||
verify(consumer, times(1)).accept(1);
|
||||
verify(consumer, times(1)).accept(2);
|
||||
verify(consumer, times(1)).accept(3);
|
||||
verifyNoMoreInteractions(consumer);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSpliterator() {
|
||||
final var integers = List.of(1, 2, 3);
|
||||
final var split = createFluentIterable(integers).spliterator();
|
||||
assertNotNull(split);
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable.lazy;
|
||||
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterableTest;
|
||||
|
||||
/**
|
||||
* LazyFluentIterableTest
|
||||
*
|
||||
*/
|
||||
class LazyFluentIterableTest extends FluentIterableTest {
|
||||
|
||||
@Override
|
||||
protected FluentIterable<Integer> createFluentIterable(Iterable<Integer> integers) {
|
||||
return LazyFluentIterable.from(integers);
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.fluentinterface.fluentiterable.simple;
|
||||
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
|
||||
import com.iluwatar.fluentinterface.fluentiterable.FluentIterableTest;
|
||||
|
||||
/**
|
||||
* SimpleFluentIterableTest
|
||||
*
|
||||
*/
|
||||
class SimpleFluentIterableTest extends FluentIterableTest {
|
||||
|
||||
@Override
|
||||
protected FluentIterable<Integer> createFluentIterable(Iterable<Integer> integers) {
|
||||
return SimpleFluentIterable.fromCopyOf(integers);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user