diff --git a/collection-pipeline/README.md b/collection-pipeline/README.md new file mode 100644 index 000000000..3599a6d5d --- /dev/null +++ b/collection-pipeline/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Collection Pipeline +folder: collection-pipeline +permalink: /patterns/collection-pipeline/ +categories: Functional +tags: + - Java + - Difficulty-Beginner + - Functional +--- + +## Intent +Collection Pipeline introduces Function Composition and Collection Pipeline, two functional-style patterns that you can combine to iterate collections in your code. +In functional programming, it's common to sequence complex operations through a series of smaller modular functions or operations. The series is called a composition of functions, or a function composition. When a collection of data flows through a function composition, it becomes a collection pipeline. Function Composition and Collection Pipeline are two design patterns frequently used in functional-style programming. + +![alt text](./etc/collection-pipeline.png "Collection Pipeline") + +## Applicability +Use the Collection Pipeline pattern when + +* When you want to perform a sequence of operations where one operation's collected output is fed into the next +* When you use a lot of statements in your code +* When you use a lot of loops in your code + +## Credits + +* [Function composition and the Collection Pipeline pattern](https://www.ibm.com/developerworks/library/j-java8idioms2/index.html) +* [Martin Fowler](https://martinfowler.com/articles/collection-pipeline/) \ No newline at end of file diff --git a/collection-pipeline/etc/collection-pipeline.png b/collection-pipeline/etc/collection-pipeline.png new file mode 100644 index 000000000..7fcdb0b94 Binary files /dev/null and b/collection-pipeline/etc/collection-pipeline.png differ diff --git a/collection-pipeline/etc/collection-pipeline.ucls b/collection-pipeline/etc/collection-pipeline.ucls new file mode 100644 index 000000000..676dc48e8 --- /dev/null +++ b/collection-pipeline/etc/collection-pipeline.ucls @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/collection-pipeline/pom.xml b/collection-pipeline/pom.xml new file mode 100644 index 000000000..a663fb35a --- /dev/null +++ b/collection-pipeline/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.20.0-SNAPSHOT + + collection-pipeline + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + \ No newline at end of file diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/App.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/App.java new file mode 100644 index 000000000..109d60b88 --- /dev/null +++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/App.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.collectionpipeline; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * In imperative-style programming, it is common to use for and while loops for + * most kinds of data processing. Function composition is a simple technique + * that lets you sequence modular functions to create more complex operations. + * When you run data through the sequence, you have a collection pipeline. + * Together, the Function Composition and Collection Pipeline patterns enable + * you to create sophisticated programs where data flow from upstream to + * downstream and is passed through a series of transformations. + * + */ +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Program entry point. + * + * @param args + * command line args + */ + public static void main(String[] args) { + + List cars = Iterating.createCars(); + + List modelsImperative = ImperativeProgramming.getModelsAfter2000UsingFor(cars); + LOGGER.info(modelsImperative.toString()); + + List modelsFunctional = FunctionalProgramming.getModelsAfter2000UsingPipeline(cars); + LOGGER.info(modelsFunctional.toString()); + } +} diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java new file mode 100644 index 000000000..be113c7cd --- /dev/null +++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.collectionpipeline; + +/** + * A Car class that has the properties of make, model, and year. + */ +public class Car { + private String make; + private String model; + private int year; + + /** + * Constructor to create an instance of car. + * @param theMake the make of the car + * @param theModel the model of the car + * @param yearOfMake the year of built of the car + */ + public Car(String theMake, String theModel, int yearOfMake) { + make = theMake; + model = theModel; + year = yearOfMake; + } + + public String getMake() { + return make; + } + + public String getModel() { + return model; + } + + public int getYear() { + return year; + } +} \ No newline at end of file diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java new file mode 100644 index 000000000..7d9834bc0 --- /dev/null +++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 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.collectionpipeline; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Iterating and sorting with a collection pipeline + * + *

In functional programming, it's common to sequence complex operations through + * a series of smaller modular functions or operations. The series is called a + * composition of functions, or a function composition. When a collection of + * data flows through a function composition, it becomes a collection pipeline. + * Function Composition and Collection Pipeline are two design patterns + * frequently used in functional-style programming. + * + *

Instead of passing a lambda expression to the map method, we passed the + * method reference Car::getModel. Likewise, instead of passing the lambda + * expression car -> car.getYear() to the comparing method, we passed the method + * reference Car::getYear. Method references are short, concise, and expressive. + * It is best to use them wherever possible. + * + */ +public class FunctionalProgramming { + private FunctionalProgramming() { + } + + /** + * Method to get models using for collection pipeline. + * + * @param cars {@link List} of {@link Car} to be used for filtering + * @return {@link List} of {@link String} representing models built after year 2000 + */ + public static List getModelsAfter2000UsingPipeline(List cars) { + return cars.stream().filter(car -> car.getYear() > 2000) + .sorted(Comparator.comparing(Car::getYear)) + .map(Car::getModel).collect(Collectors.toList()); + } +} diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java new file mode 100644 index 000000000..092469fbd --- /dev/null +++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java @@ -0,0 +1,80 @@ +/** + * The MIT License + * Copyright (c) 2014 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.collectionpipeline; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Imperative-style programming to iterate over the list and get the names of + * cars made later than the year 2000. We then sort the models in ascending + * order by year. + * + *

As you can see, there's a lot of looping in this code. First, the + * getModelsAfter2000UsingFor method takes a list of cars as its parameter. It + * extracts or filters out cars made after the year 2000, putting them into a + * new list named carsSortedByYear. Next, it sorts that list in ascending order + * by year-of-make. Finally, it loops through the list carsSortedByYear to get + * the model names and returns them in a list. + * + *

This short example demonstrates what I call the effect of statements. While + * functions and methods in general can be used as expressions, the {@link Collections} + * sort method doesn't return a result. Because it is used as a statement, it + * mutates the list given as argument. Both of the for loops also mutate lists + * as they iterate. Being statements, that's just how these elements work. As a + * result, the code contains unnecessary garbage variables + */ +public class ImperativeProgramming { + private ImperativeProgramming() { + } + + /** + * Method to return the car models built after year 2000 using for loops. + * @param cars {@link List} of {@link Car} to iterate over + * @return {@link List} of {@link String} of car models built after year 2000 + */ + public static List getModelsAfter2000UsingFor(List cars) { + List carsSortedByYear = new ArrayList<>(); + + for (Car car : cars) { + if (car.getYear() > 2000) { + carsSortedByYear.add(car); + } + } + + Collections.sort(carsSortedByYear, new Comparator() { + public int compare(Car car1, Car car2) { + return new Integer(car1.getYear()).compareTo(car2.getYear()); + } + }); + + List models = new ArrayList<>(); + for (Car car : carsSortedByYear) { + models.add(car.getModel()); + } + + return models; + } +} diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Iterating.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Iterating.java new file mode 100644 index 000000000..4293603cb --- /dev/null +++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Iterating.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.collectionpipeline; + +import java.util.Arrays; +import java.util.List; + +/** + * A factory class to create a collection of {@link Car} instances. + */ +public class Iterating { + private Iterating() { + } + + /** + * Factory method to create a {@link List} of {@link Car} instances. + * @return {@link List} of {@link Car} + */ + public static List createCars() { + return Arrays.asList(new Car("Jeep", "Wrangler", 2011), new Car("Jeep", "Comanche", 1990), + new Car("Dodge", "Avenger", 2010), + new Car("Buick", "Cascada", 2016), + new Car("Ford", "Focus", 2012), + new Car("Chevrolet", "Geo Metro", 1992)); + } +} \ No newline at end of file diff --git a/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java b/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java new file mode 100644 index 000000000..26f546a16 --- /dev/null +++ b/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.collectionpipeline; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * Tests that Collection Pipeline methods work as expected. + */ +public class AppTest { + + private List cars = Iterating.createCars(); + + @Test + public void testGetModelsAfter2000UsingFor() { + List models = ImperativeProgramming.getModelsAfter2000UsingFor(cars); + assertEquals(models, Arrays.asList("Avenger", "Wrangler", "Focus", "Cascada")); + } + + @Test + public void testGetModelsAfter2000UsingPipeline() { + List models = FunctionalProgramming.getModelsAfter2000UsingPipeline(cars); + assertEquals(models, Arrays.asList("Avenger", "Wrangler", "Focus", "Cascada")); + } +} diff --git a/pom.xml b/pom.xml index e1d025c13..9d2ca8066 100644 --- a/pom.xml +++ b/pom.xml @@ -161,6 +161,7 @@ dirty-flag trampoline serverless + collection-pipeline