diff --git a/function-composition/.gitignore b/function-composition/.gitignore new file mode 100644 index 000000000..431eba612 --- /dev/null +++ b/function-composition/.gitignore @@ -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 diff --git a/function-composition/README.md b/function-composition/README.md new file mode 100644 index 000000000..99b7b9ff2 --- /dev/null +++ b/function-composition/README.md @@ -0,0 +1,109 @@ +--- +title: Function Composition +category: Behavioral +language: en +tag: + - Functional Programming + - Functional decomposition + - Java +--- + +## 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 composeFunctions(Function f1, Function f2) { + return f1.andThen(f2); + } +} +``` +```java +public class App { + public static void main(String[] args) { + Function timesTwo = x -> x * 2; + Function square = x -> x * x; + + Function 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) diff --git a/function-composition/etc/function.composition.urm.png b/function-composition/etc/function.composition.urm.png new file mode 100644 index 000000000..53e646086 Binary files /dev/null and b/function-composition/etc/function.composition.urm.png differ diff --git a/function-composition/etc/function.composition.urm.puml b/function-composition/etc/function.composition.urm.puml new file mode 100644 index 000000000..5fc3b5c6d --- /dev/null +++ b/function-composition/etc/function.composition.urm.puml @@ -0,0 +1,21 @@ +@startuml +skinparam monochrome true + +participant "App" as App +participant "FunctionComposer" as Composer +participant "Function" 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 \ No newline at end of file diff --git a/function-composition/pom.xml b/function-composition/pom.xml new file mode 100644 index 000000000..da803b723 --- /dev/null +++ b/function-composition/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + function-composition + + + org.junit.jupiter + junit-jupiter-engine + test + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + App + + + + + + + + + \ No newline at end of file diff --git a/function-composition/src/main/java/com/iluwatar/function/composition/App.java b/function-composition/src/main/java/com/iluwatar/function/composition/App.java new file mode 100644 index 000000000..da55135bd --- /dev/null +++ b/function-composition/src/main/java/com/iluwatar/function/composition/App.java @@ -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 timesTwo = x -> x * 2; + Function square = x -> x * x; + + Function composedFunction = FunctionComposer.composeFunctions(timesTwo, square); + + int result = composedFunction.apply(3); + logger.info("Result of composing 'timesTwo' and 'square' functions applied to 3 is: " + result); + } +} diff --git a/function-composition/src/main/java/com/iluwatar/function/composition/FunctionComposer.java b/function-composition/src/main/java/com/iluwatar/function/composition/FunctionComposer.java new file mode 100644 index 000000000..725903f9c --- /dev/null +++ b/function-composition/src/main/java/com/iluwatar/function/composition/FunctionComposer.java @@ -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 composeFunctions(Function f1, Function f2) { + return f1.andThen(f2); + } +} diff --git a/function-composition/src/test/java/com/iluwatar/function/composition/AppTest.java b/function-composition/src/test/java/com/iluwatar/function/composition/AppTest.java new file mode 100644 index 000000000..82d543568 --- /dev/null +++ b/function-composition/src/test/java/com/iluwatar/function/composition/AppTest.java @@ -0,0 +1,40 @@ +/* + * 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 org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * Application test + */ +class AppTest { + + @Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/function-composition/src/test/java/com/iluwatar/function/composition/FunctionComposerTest.java b/function-composition/src/test/java/com/iluwatar/function/composition/FunctionComposerTest.java new file mode 100644 index 000000000..af8950252 --- /dev/null +++ b/function-composition/src/test/java/com/iluwatar/function/composition/FunctionComposerTest.java @@ -0,0 +1,102 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import org.junit.Test; +import java.util.function.Function; + +/** + * Test class for FunctionComposer. + */ +public class FunctionComposerTest { + + /** + * Tests the composition of two functions. + */ + @Test + public void testComposeFunctions() { + Function timesTwo = x -> x * 2; + Function square = x -> x * x; + + Function composed = FunctionComposer.composeFunctions(timesTwo, square); + + assertEquals("Expected output of composed functions is 36", 36, (int) composed.apply(3)); + } + + /** + * Tests function composition with identity function. + */ + @Test + public void testComposeWithIdentity() { + Function identity = Function.identity(); + Function timesThree = x -> x * 3; + + Function composedLeft = FunctionComposer.composeFunctions(identity, timesThree); + Function composedRight = FunctionComposer.composeFunctions(timesThree, identity); + + assertEquals("Composition with identity on the left should be the same", 9, (int) composedLeft.apply(3)); + assertEquals("Composition with identity on the right should be the same", 9, (int) composedRight.apply(3)); + } + + /** + * Tests function composition resulting in zero. + */ + @Test + public void testComposeToZero() { + Function multiply = x -> x * 10; + Function toZero = x -> 0; + + Function composed = FunctionComposer.composeFunctions(multiply, toZero); + + assertEquals("Expected output of function composition leading to zero is 0", 0, (int) composed.apply(5)); + } + + /** + * Tests the composition with a negative function. + */ + @Test + public void testComposeNegative() { + Function negate = x -> -x; + Function square = x -> x * x; + + Function composed = FunctionComposer.composeFunctions(negate, square); + + assertEquals("Expected square of negative number to be positive", 9, (int) composed.apply(3)); + } + + /** + * Tests the composition of functions that cancel each other out. + */ + @Test + public void testComposeInverseFunctions() { + Function timesTwo = x -> x * 2; + Function half = x -> x / 2; + + Function composed = FunctionComposer.composeFunctions(timesTwo, half); + + assertEquals("Expect the functions to cancel each other out", 5, (int) composed.apply(5)); + } +} diff --git a/pom.xml b/pom.xml index 7e590a318..4898fa441 100644 --- a/pom.xml +++ b/pom.xml @@ -215,6 +215,7 @@ serialized-lob server-session virtual-proxy + function-composition