diff --git a/collection-pipeline/README.md b/collection-pipeline/README.md index 00598c500..033204c19 100644 --- a/collection-pipeline/README.md +++ b/collection-pipeline/README.md @@ -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 getModelsAfter2000(List 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>getGroupingOfCarsByCategory(List cars){ - return cars.stream().collect(groupingBy(Car::getCategory)); - } +public static Map> getGroupingOfCarsByCategory(List 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 getSedanCarsOwnedSortedByDate(List 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/) diff --git a/collection-pipeline/etc/collection-pipeline.png b/collection-pipeline/etc/collection-pipeline.png deleted file mode 100644 index 67d52629c..000000000 Binary files a/collection-pipeline/etc/collection-pipeline.png and /dev/null differ diff --git a/collection-pipeline/etc/collection-pipeline.ucls b/collection-pipeline/etc/collection-pipeline.ucls deleted file mode 100644 index 6373db62f..000000000 --- a/collection-pipeline/etc/collection-pipeline.ucls +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/collection-pipeline/etc/collection-pipeline.urm.png b/collection-pipeline/etc/collection-pipeline.urm.png new file mode 100644 index 000000000..074dffe7b Binary files /dev/null and b/collection-pipeline/etc/collection-pipeline.urm.png differ