Skip to content

Commit

Permalink
feature: Implement function composition pattern iluwatar#2897
Browse files Browse the repository at this point in the history
  • Loading branch information
Adelechka committed May 14, 2024
1 parent bb0a6c1 commit d564e7d
Show file tree
Hide file tree
Showing 10 changed files with 506 additions and 0 deletions.
59 changes: 59 additions & 0 deletions function-composition/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
################## Eclipse ######################
target
.metadata
.settings
.classpath
.project
*.class
tmp/
*.tmp
*.bak
*~.nib
local.properties
.loadpath
.recommenders
.DS_Store

####### Java annotation processor (APT) ########
.factorypath

################ Package Files ##################
*.jar
*.war
*.ear
*.swp
datanucleus.log
/bin/
*.log
event-sourcing/Journal.json

################## Checkstyle ###################
.checkstyle

##################### STS #######################
.apt_generated
.springBeans
.sts4-cache

################# IntelliJ IDEA #################
.idea
*.iws
*.iml
*.ipr

################### NetBeans ####################
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

#################### VS Code ####################
.vscode/

#################### Java Design Patterns #######
etc/Java Design Patterns.urm.puml
serialized-entity/output.txt
113 changes: 113 additions & 0 deletions function-composition/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
title: Function Composer
category: Behavioral
language: en
tag:
- Functional Programming
- Composition
- Java
---

## Name / classification

Function Composer

## Also known as

Functional Composition

## Intent
To enable creating complex functions by composing simpler ones, enhancing modularity and reusability of function-based logic.

## Explanation

Real-world example:

> In financial software, functions that calculate various financial metrics can be composed to provide detailed analysis. For instance, a function that calculates interest can be composed with another that adjusts for taxes, allowing for a modular yet comprehensive financial assessment tool.
In plain words:

> The Function Composer pattern allows building complex functions by combining simpler ones, making it easier to manage, test, and reuse individual pieces of functionality.
Wikipedia says:

> Function composition is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.
**Programmatic Example**

Here is how the Function Composer pattern might be implemented and used in Java:

```java
public class FunctionComposer {

public static Function<Integer, Integer> composeFunctions(Function<Integer, Integer> f1, Function<Integer, Integer> f2) {
return f1.andThen(f2);
}
}
```
```java
public class App {
public static void main(String[] args) {
Function<Integer, Integer> timesTwo = x -> x * 2;
Function<Integer, Integer> square = x -> x * x;

Function<Integer, Integer> composedFunction = FunctionComposer.composeFunctions(timesTwo, square);

int result = composedFunction.apply(3);
logger.info("Result of composing 'timesTwo' and 'square' functions applied to 3 is: " + result);
}
}
```

Result:
```
Result of composing 'timesTwo' and 'square' functions applied to 3 is: 36 // Result will be 36 (3 * 2 = 6, 6 * 6 = 36)
```

Use ``.compose()`` function when you need pre-compose and ``.andThen()`` function when you need post-compose.

## Sequence diagram

![Functional Composer Diagram](./etc/function.composition.urm.png "function composition")

## Applicability

Use the Function Composer pattern when:

* You want to create a pipeline of operations where the output of one function is the input to another.
* You need to enhance the clarity and quality of your code by structuring complex function logic into simpler, reusable components.
* You are working in a functional programming environment or a language that supports higher-order functions.

## Tutorials

[Function Composition in Java](https://functionalprogramming.medium.com/function-composition-in-java-beaf39426f52)

## Known uses

* Stream processing in Java 8 and above
* Query builders in ORM libraries
* Middleware composition in web frameworks

## Consequences

Benefits:

* High reusability of composed functions.
* Increased modularity, making complex functions easier to understand and maintain.
* Flexible and dynamic creation of function pipelines at runtime.
*
Drawbacks:

* Potentially higher complexity when debugging composed functions.
* Overhead from creating and managing multiple function objects in memory-intensive scenarios.

## Related patterns

* Chain of Responsibility
* Decorator
* Strategy

## Credits

[Functional Programming in Java](https://www.baeldung.com/java-functional-programming)
[Function Composition in Java](https://functionalprogramming.medium.com/function-composition-in-java-beaf39426f52)
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions function-composition/etc/function.composition.urm.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@startuml
skinparam monochrome true

participant "App" as App
participant "FunctionComposer" as Composer
participant "Function<Integer, Integer>" as F1

create F1
App -> F1 : func1 = x -> x * 2
create F1
App -> F1 : func2 = x -> x * x

App -> Composer : func1, func2
activate Composer
Composer -> F1 : func1.andThen(func2)
deactivate Composer
activate F1
F1 -> App : composedFunction
deactivate F1

@enduml
74 changes: 74 additions & 0 deletions function-composition/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>function-composition</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version> <!-- Use the latest version -->
<scope>provided</scope>
</dependency>

</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.function.composition;

import java.util.function.Function;
import org.slf4j.LoggerFactory;

/**
* Main application class to demonstrate the use of function composition.
*/
public class App {

/**
* Main method to demonstrate function composition using FunctionComposer.
*
* @param args command line arguments (not used)
*/
public static void main(String[] args) {
final var logger = LoggerFactory.getLogger(App.class);
Function<Integer, Integer> timesTwo = x -> x * 2;
Function<Integer, Integer> square = x -> x * x;

Function<Integer, Integer> composedFunction = FunctionComposer.composeFunctions(timesTwo, square);

int result = composedFunction.apply(3);
logger.info("Result of composing 'timesTwo' and 'square' functions applied to 3 is: " + result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.function.composition;

import java.util.function.Function;

/**
* Class for composing functions using the Function Composition pattern.
* Provides a static method to compose two functions using the 'andThen' method.
*/
public class FunctionComposer {

/**
* Composes two functions where the output of the first function becomes
* the input of the second function.
*
* @param f1 the first function to apply
* @param f2 the second function to apply after the first
* @return a composed function that applies f1 and then f2
*/
public static Function<Integer, Integer> composeFunctions(Function<Integer, Integer> f1, Function<Integer, Integer> f2) {
return f1.andThen(f2);
}
}

0 comments on commit d564e7d

Please sign in to comment.