docs: collection pipeline

This commit is contained in:
Ilkka Seppälä
2024-05-24 19:55:57 +03:00
parent 80ae1a9bca
commit b483bd8e63
4 changed files with 33 additions and 138 deletions
+33 -40
View File
@@ -3,8 +3,10 @@ title: Collection Pipeline
category: Functional
language: en
tag:
- Reactive
- Functional decomposition
- Data processing
- Data transformation
- Reactive
---
## Intent
@@ -15,7 +17,7 @@ The Collection Pipeline design pattern is intended to process collections of dat
Real-world example
> Imagine you're in a large library filled with books, and you're tasked with finding all the science fiction books published after 2000, then arranging them by author name in alphabetical order, and finally picking out the top 5 based on their popularity or ratings.
> Imagine a real-world example of a factory assembly line for manufacturing cars. In this assembly line, each station performs a specific task on the car chassis, such as installing the engine, painting the body, attaching the wheels, and inspecting the final product. Each station takes the output from the previous station and adds its own processing step. This sequence of operations is analogous to the Collection Pipeline design pattern, where each step in the pipeline transforms the data and passes it on to the next step, ensuring an efficient and organized workflow.
In plain words
@@ -27,63 +29,56 @@ Wikipedia says
**Programmatic Example**
The Collection Pipeline pattern is implemented in this code example by using Java's Stream API to perform a series of transformations on a collection of Car objects. The transformations are chained together to form a pipeline. Here's a breakdown of how it's done:
The Collection Pipeline is a programming pattern where you organize some computation as a sequence of operations which compose by taking a collection as output of one operation and feeding it into the next.
1. Creation of Cars: A list of Car objects is created using the `CarFactory.createCars()` method.
Here's a programmatic example of the Collection Pipeline design pattern:
`var cars = CarFactory.createCars();`
**Step 1: Filtering**
2. Filtering and Transforming: The `FunctionalProgramming.getModelsAfter2000(cars)` method filters the cars to only include those made after the year 2000, and then transforms the filtered cars into a list of their model names.
`var modelsFunctional = FunctionalProgramming.getModelsAfter2000(cars);`
In the `getModelsAfter2000` method, the pipeline is created as follows:
We start with a list of `Car` objects and we want to filter out those that were manufactured after the year 2000. This is done using the `stream()` method to create a stream from the list, the `filter()` method to filter out the cars we want, and the `collect()` method to collect the results into a new list.
```java
public static List<String> getModelsAfter2000(List<Car> cars){
return cars.stream().filter(car->car.getYear()>2000)
.sorted(comparing(Car::getYear))
.map(Car::getModel)
.collect(toList());
}
return cars.stream()
.filter(car -> car.getYear() > 2000) // Filter cars manufactured after 2000
.sorted(comparing(Car::getYear)) // Sort the cars by year
.map(Car::getModel) // Get the model of each car
.collect(toList()); // Collect the results into a new list
}
```
3. Grouping: The `FunctionalProgramming.getGroupingOfCarsByCategory(cars)` method groups the cars by their category.
**Step 2: Grouping**
`var groupingByCategoryFunctional = FunctionalProgramming.getGroupingOfCarsByCategory(cars);`
In the getGroupingOfCarsByCategory method, the pipeline is created as follows:
Next, we want to group the cars by their category. This is done using the `groupingBy()` collector.
```java
public static Map<Category, List<Car>>getGroupingOfCarsByCategory(List<Car> cars){
return cars.stream().collect(groupingBy(Car::getCategory));
}
public static Map<Category, List<Car>> getGroupingOfCarsByCategory(List<Car> cars){
return cars.stream()
.collect(groupingBy(Car::getCategory)); // Group cars by category
}
```
4. Filtering, Sorting and Transforming: The `FunctionalProgramming.getSedanCarsOwnedSortedByDate(List.of(john))` method filters the cars owned by a person to only include sedans, sorts them by date, and then transforms the sorted cars into a list of Car objects.
**Step 3: Filtering, Sorting and Transforming**
`var sedansOwnedFunctional = FunctionalProgramming.getSedanCarsOwnedSortedByDate(List.of(john));`
In the `getSedanCarsOwnedSortedByDate` method, the pipeline is created as follows:
Finally, we want to filter the cars owned by a person to only include sedans, sort them by date, and then transform the sorted cars into a list of `Car` objects.
```java
public static List<Car> getSedanCarsOwnedSortedByDate(List<Person> persons){
return persons.stream().flatMap(person->person.getCars().stream())
.filter(car->Category.SEDAN.equals(car.getCategory()))
.sorted(comparing(Car::getDate))
.collect(toList());
}
return persons.stream()
.flatMap(person -> person.getCars().stream()) // Flatten the list of cars owned by each person
.filter(car -> Category.SEDAN.equals(car.getCategory())) // Filter to only include sedans
.sorted(comparing(Car::getDate)) // Sort the cars by date
.collect(toList()); // Collect the results into a new list
}
```
In each of these methods, the Collection Pipeline pattern is used to perform a series of operations on the collection of cars in a declarative manner, which improves readability and maintainability.
## Class diagram
![alt text](./etc/collection-pipeline.png "Collection Pipeline")
## Applicability
This pattern is applicable in scenarios involving bulk data operations such as filtering, mapping, sorting, or reducing collections. It's particularly useful in data analysis, transformation tasks, and where a sequence of operations needs to be applied to each element of a collection.
* When you need to perform a series of transformations on a collection of data.
* When you want to improve readability and maintainability of complex data processing code.
* When working with large datasets where intermediate results should not be stored in memory.
## Known Uses
@@ -115,9 +110,7 @@ Trade-offs:
## Credits
* [Function composition and the Collection Pipeline pattern](https://www.ibm.com/developerworks/library/j-java8idioms2/index.html)
* [Collection Pipeline described by Martin Fowler](https://martinfowler.com/articles/collection-pipeline/)
* [Java8 Streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html)
* [Refactoring: Improving the Design of Existing Code](https://amzn.to/3VDMWDO)
* [Functional Programming in Scala](https://amzn.to/4cEo6K2)
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3THp4wy)
* [Refactoring: Improving the Design of Existing Code](https://amzn.to/3VDMWDO)
* [Collection Pipeline (Martin Fowler)](https://martinfowler.com/articles/collection-pipeline/)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.2.2" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
<class id="1" language="java" name="com.iluwatar.collectionpipeline.App" project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/App.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="150" y="100"/>
<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="2" language="java" name="com.iluwatar.collectionpipeline.ImperativeProgramming"
project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="437" y="109"/>
<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.collectionpipeline.Car" project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="724" y="339"/>
<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="4" language="java" name="com.iluwatar.collectionpipeline.CarFactory" project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/CarFactory.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="899" y="91"/>
<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="5" language="java" name="com.iluwatar.collectionpipeline.FunctionalProgramming"
project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1187" y="109"/>
<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>
<enumeration id="6" language="java" name="com.iluwatar.collectionpipeline.Category" project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Category.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="486" y="339"/>
<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"/>
</display>
</enumeration>
<class id="7" language="java" name="com.iluwatar.collectionpipeline.Person" project="collection-pipeline"
file="/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="723" y="91"/>
<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>
<association id="8">
<end type="SOURCE" refId="7" navigable="false">
<attribute id="9" name="cars"/>
<multiplicity id="10" minimum="0" maximum="2147483647"/>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="11">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="12" name="category"/>
<multiplicity id="13" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-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"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB