diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml new file mode 100644 index 000000000..c78c182e3 --- /dev/null +++ b/fluentinterface/pom.xml @@ -0,0 +1,20 @@ + + + + java-design-patterns + com.iluwatar + 1.5.0 + + 4.0.0 + + fluentinterface + + + junit + junit + test + + + \ No newline at end of file diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java new file mode 100644 index 000000000..fded13624 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java @@ -0,0 +1,90 @@ +package com.iluwatar.fluentinterface; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Function; +import java.util.function.Predicate; + +public class App { + + public static void main(String[] args) { + + List integerList = new ArrayList() {{ + add(1); + add(-61); + add(14); + add(-22); + add(18); + add(-87); + add(6); + add(64); + add(-82); + add(26); + add(-98); + add(97); + add(45); + add(23); + add(2); + add(-68); + add(45); + }}; + prettyPrint("The initial list contains: ", integerList); + + List firstFiveNegatives = FluentIterable.from(integerList) + .filter(negatives()) + .first(3) + .asList(); + prettyPrint("The first three negative values are: ", firstFiveNegatives); + + + List lastTwoPositives = FluentIterable.from(integerList) + .filter(positives()) + .last(2) + .asList(); + prettyPrint("The last two positive values are: ", lastTwoPositives); + + FluentIterable.from(integerList) + .filter(number -> number%2 == 0) + .first() + .ifPresent(evenNumber -> System.out.println(String.format("The first even number is: %d", evenNumber))); + + + List transformedList = FluentIterable.from(integerList) + .filter(negatives()) + .map(transformToString()) + .asList(); + prettyPrint("A string-mapped list of negative numbers contains: ", transformedList); + + } + + private static Function transformToString() { + return integer -> "String[" + String.valueOf(integer) + "]"; + } + private static Predicate negatives() { + return integer -> (integer < 0); + } + private static Predicate positives() { + return integer -> (integer > 0); + } + + private static void prettyPrint(String prefix, Iterable iterable) { + prettyPrint(", ", prefix, ".", iterable); + } + private static void prettyPrint(String prefix, String suffix, Iterable iterable) { + prettyPrint(", ", prefix, suffix, iterable); + } + + private static void prettyPrint(String delimiter, String prefix, String suffix, Iterable iterable) { + StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + joiner.add(iterator.next().toString()); + } + + System.out.println(joiner); + } +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java new file mode 100644 index 000000000..edb9275c1 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -0,0 +1,192 @@ +package com.iluwatar.fluentinterface.fluentiterable; + +import java.util.*; +import java.util.function.Consumer; +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 implementation demonstrates a possible way to implement this functionality, but + * doesn't aim to be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. + * @param is the class of objects the iterable contains + */ +public class FluentIterable implements Iterable { + + private final Iterable iterable; + + /** + * This constructor creates a copy of a given iterable's contents. + * @param iterable the iterable this interface copies to work on. + */ + protected FluentIterable(Iterable iterable) { + ArrayList copy = new ArrayList<>(); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + this.iterable = copy; + } + + /** + * Iterates over all elements of this iterator and filters them. + * @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 + */ + public final FluentIterable filter(Predicate predicate) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + TYPE nextElement = iterator.next(); + if(!predicate.test(nextElement)) { + iterator.remove(); + } + } + return this; + } + + /** + * Uses the Iterable interface's forEach method to apply a given function + * for each object of the iterator. + * @param action the action for each object + * @return the same FluentIterable with an untouched collection + */ + public final FluentIterable forEachDo(Consumer action) { + iterable.forEach(action); + return this; + } + + /** + * Can be used to collect objects from the iteration. + * @return an option of the first object of the iteration + */ + public final Optional first() { + List list = first(1).asList(); + if(list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * 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. + */ + public final FluentIterable first(int count) { + Iterator iterator = iterator(); + int currentCount = 0; + while (iterator.hasNext()) { + iterator.next(); + if(currentCount >= count) { + iterator.remove(); + } + currentCount++; + } + return this; + } + + /** + * Can be used to collect objects from the iteration. + * @return an option of the last object of the iteration + */ + public final Optional last() { + List list = last(1).asList(); + if(list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * 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' last objects + */ + public final FluentIterable last(int count) { + int remainingElementsCount = getRemainingElementsCount(); + Iterator iterator = iterator(); + int 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 NEW_TYPE. + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + public final FluentIterable map(Function function) { + List temporaryList = new ArrayList(); + Iterator iterator = iterator(); + while (iterator.hasNext()) { + temporaryList.add(function.apply(iterator.next())); + } + return from(temporaryList); + } + + /** + * Collects all remaining objects of this iteration into a list. + * @return a list with all remaining objects of this iteration + */ + public List asList() { + return toList(iterable.iterator()); + } + + /** + * @return a FluentIterable from a given iterable. Calls the FluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new FluentIterable<>(iterable); + } + + @Override + public Iterator iterator() { + return iterable.iterator(); + } + + @Override + public void forEach(Consumer action) { + iterable.forEach(action); + } + + + @Override + public Spliterator spliterator() { + return iterable.spliterator(); + } + + /** + * @return the count of remaining objects in the current iteration + */ + public final int getRemainingElementsCount() { + int counter = 0; + Iterator iterator = iterator(); + while(iterator.hasNext()) { + iterator.next(); + counter++; + } + return counter; + } + + /** + * Collects the remaining objects of the given iterators iteration into an List. + * @return a new List with the remaining objects. + */ + public static List toList(Iterator iterator) { + List copy = new ArrayList<>(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + return copy; + } +} diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java new file mode 100644 index 000000000..32bbca430 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java @@ -0,0 +1,12 @@ +package com.iluwatar.fluentinterface; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/pom.xml b/pom.xml index 7b0d80bd8..2c040005b 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,8 @@ step-builder layers message-channel - + fluentinterface +