From 05e582ca3ef261165fd5a291a4d8dc6360bd0fb9 Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 26 Nov 2019 06:03:04 +0000 Subject: [PATCH] Pattern combinator (#1105) * init * add pattern --- combinator/README.md | 33 ++++++ combinator/pom.xml | 44 ++++++++ .../iluwatar/combinator/CombinatorApp.java | 92 ++++++++++++++++ .../java/com/iluwatar/combinator/Finder.java | 93 ++++++++++++++++ .../java/com/iluwatar/combinator/Finders.java | 103 ++++++++++++++++++ .../combinator/CombinatorAppTest.java | 36 ++++++ .../com/iluwatar/combinator/FinderTest.java | 44 ++++++++ .../com/iluwatar/combinator/FindersTest.java | 83 ++++++++++++++ pom.xml | 55 +++++----- 9 files changed, 556 insertions(+), 27 deletions(-) create mode 100644 combinator/README.md create mode 100644 combinator/pom.xml create mode 100644 combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java create mode 100644 combinator/src/main/java/com/iluwatar/combinator/Finder.java create mode 100644 combinator/src/main/java/com/iluwatar/combinator/Finders.java create mode 100644 combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java create mode 100644 combinator/src/test/java/com/iluwatar/combinator/FinderTest.java create mode 100644 combinator/src/test/java/com/iluwatar/combinator/FindersTest.java diff --git a/combinator/README.md b/combinator/README.md new file mode 100644 index 000000000..e22450321 --- /dev/null +++ b/combinator/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Combinator +folder: combinator +permalink: /patterns/combinator/ +categories: Behavioral +tags: + - Functional + - Reactive + - Idiom +--- + +## Also known as +Composition pattern + +## Intent +The functional pattern representing a style of organizing libraries centered around the idea of combining functions. +Putting it simply, there is some type T, some functions for constructing "primitive" values of type T, +and some "combinators" which can combine values of type T in various ways to build up more complex values of type T. + + +## Applicability +Use the combinator pattern when: +- You are able to create a more complex value from more plain values but having the same type(a combination of them) + +## Real world examples +- java.util.function.Function#compose +- java.util.function.Function#andThen + +## Credits +- [Example for java](https://gtrefs.github.io/code/combinator-pattern/) +- [Combinator pattern](https://wiki.haskell.org/Combinator_pattern) +- [Combinatory logic](https://wiki.haskell.org/Combinatory_logic) \ No newline at end of file diff --git a/combinator/pom.xml b/combinator/pom.xml new file mode 100644 index 000000000..3edfa7580 --- /dev/null +++ b/combinator/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.23.0-SNAPSHOT + + + combinator + + + junit + junit + test + + + + diff --git a/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java b/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java new file mode 100644 index 000000000..578c87bfe --- /dev/null +++ b/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java @@ -0,0 +1,92 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.combinator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The functional pattern representing a style of organizing libraries + * centered around the idea of combining functions. + * Putting it simply, there is some type T, some functions + * for constructing "primitive" values of type T, + * and some "combinators" which can combine values of type T + * in various ways to build up more complex values of type T. + * The class {@link Finder} defines a simple function {@link Finder#find(String)} + * and connected functions + * {@link Finder#or(Finder)}, + * {@link Finder#not(Finder)}, + * {@link Finder#and(Finder)} + * Using them the became possible to get more complex functions {@link Finders} + */ +public class CombinatorApp { + + /** + * Logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(CombinatorApp.class); + + /** + * main. + * @param args args + */ + public static void main(String[] args) { + var queriesOr = new String[]{"many", "Annabel"}; + var finder = Finders.expandedFinder(queriesOr); + var res = finder.find(text()); + LOGGER.info("the result of expanded(or) query[{}] is {}", queriesOr, res); + + var queriesAnd = new String[]{"Annabel", "my"}; + finder = Finders.specializedFinder(queriesAnd); + res = finder.find(text()); + LOGGER.info("the result of specialized(and) query[{}] is {}", queriesAnd, res); + + finder = Finders.advancedFinder("it was","kingdom","sea"); + res = finder.find(text()); + LOGGER.info("the result of advanced query is {}", res); + + res = Finders.filteredFinder(" was ", "many", "child").find(text()); + LOGGER.info("the result of filtered query is {}", res); + + + } + + private static String text() { + return + "It was many and many a year ago,\n" + + "In a kingdom by the sea,\n" + + "That a maiden there lived whom you may know\n" + + "By the name of ANNABEL LEE;\n" + + "And this maiden she lived with no other thought\n" + + "Than to love and be loved by me.\n" + + "I was a child and she was a child,\n" + + "In this kingdom by the sea;\n" + + "But we loved with a love that was more than love-\n" + + "I and my Annabel Lee;\n" + + "With a love that the winged seraphs of heaven\n" + + "Coveted her and me."; + } + +} diff --git a/combinator/src/main/java/com/iluwatar/combinator/Finder.java b/combinator/src/main/java/com/iluwatar/combinator/Finder.java new file mode 100644 index 000000000..37cecbd0c --- /dev/null +++ b/combinator/src/main/java/com/iluwatar/combinator/Finder.java @@ -0,0 +1,93 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.combinator; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Functional interface to find lines in text. + */ +public interface Finder { + + /** + * The function to find lines in text. + * @param text full tet + * @return result of searching + */ + List find(String text); + + /** + * Simple implementation of function {@link #find(String)}. + * @param word for searching + * @return this + */ + static Finder contains(String word) { + return txt -> Stream.of(txt.split("\n")) + .filter(line -> line.toLowerCase().contains(word.toLowerCase())) + .collect(Collectors.toList()); + } + + /** + * combinator not. + * @param notFinder finder to combine + * @return new finder including previous finders + */ + default Finder not(Finder notFinder) { + return txt -> { + List res = this.find(txt); + res.removeAll(notFinder.find(txt)); + return res; + }; + } + + /** + * combinator or. + * @param orFinder finder to combine + * @return new finder including previous finders + */ + default Finder or(Finder orFinder) { + return txt -> { + List res = this.find(txt); + res.addAll(orFinder.find(txt)); + return res; + }; + } + + /** + * combinator or. + * @param andFinder finder to combine + * @return new finder including previous finders + */ + default Finder and(Finder andFinder) { + return + txt -> this + .find(txt) + .stream() + .flatMap(line -> andFinder.find(line).stream()) + .collect(Collectors.toList()); + } + +} diff --git a/combinator/src/main/java/com/iluwatar/combinator/Finders.java b/combinator/src/main/java/com/iluwatar/combinator/Finders.java new file mode 100644 index 000000000..f91c07f4c --- /dev/null +++ b/combinator/src/main/java/com/iluwatar/combinator/Finders.java @@ -0,0 +1,103 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.combinator; + +import java.util.ArrayList; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Complex finders consisting of simple finder. + */ +public class Finders { + private Finders() { + } + + + /** + * Finder to find a complex query. + * @param query to find + * @param orQuery alternative to find + * @param notQuery exclude from search + * @return new finder + */ + public static Finder advancedFinder(String query, String orQuery, String notQuery) { + return + Finder.contains(query) + .or(Finder.contains(orQuery)) + .not(Finder.contains(notQuery)); + } + + /** + * Filtered finder looking a query with excluded queries as well. + * @param query to find + * @param excludeQueries to exclude + * @return new finder + */ + public static Finder filteredFinder(String query, String... excludeQueries) { + var finder = Finder.contains(query); + + for (String q : excludeQueries) { + finder = finder.not(Finder.contains(q)); + } + return finder; + + } + + /** + * Specialized query. Every next query is looked in previous result. + * @param queries array with queries + * @return new finder + */ + public static Finder specializedFinder(String... queries) { + var finder = identMult(); + + for (String query : queries) { + finder = finder.and(Finder.contains(query)); + } + return finder; + } + + /** + * Expanded query. Looking for alternatives. + * @param queries array with queries. + * @return new finder + */ + public static Finder expandedFinder(String... queries) { + var finder = identSum(); + + for (String query : queries) { + finder = finder.or(Finder.contains(query)); + } + return finder; + } + + private static Finder identMult() { + return txt -> Stream.of(txt.split("\n")).collect(Collectors.toList()); + } + + private static Finder identSum() { + return txt -> new ArrayList<>(); + } +} diff --git a/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java new file mode 100644 index 000000000..f42b46c14 --- /dev/null +++ b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.combinator; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CombinatorAppTest { + + @Test + public void main() { + CombinatorApp.main(new String[]{}); + } +} \ No newline at end of file diff --git a/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java b/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java new file mode 100644 index 000000000..67314903d --- /dev/null +++ b/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java @@ -0,0 +1,44 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.combinator; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.*; + +public class FinderTest { + + @Test + public void contains() { + var example = "the first one \nthe second one \n"; + + var result = Finder.contains("second").find(example); + Assert.assertEquals(result.size(),1); + Assert.assertEquals(result.get(0),"the second one "); + } + +} \ No newline at end of file diff --git a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java new file mode 100644 index 000000000..5753ec92a --- /dev/null +++ b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * Copyright © 2014-2019 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.combinator; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +import static com.iluwatar.combinator.Finders.*; +import static org.junit.Assert.*; + +public class FindersTest { + + @Test + public void advancedFinderTest() { + var res = advancedFinder("it was","kingdom","sea").find(text()); + Assert.assertEquals(res.size(),1); + Assert.assertEquals(res.get(0),"It was many and many a year ago,"); + } + + @Test + public void filteredFinderTest() { + var res = filteredFinder(" was ", "many", "child").find(text()); + Assert.assertEquals(res.size(),1); + Assert.assertEquals(res.get(0),"But we loved with a love that was more than love-"); + } + + @Test + public void specializedFinderTest() { + var res = specializedFinder("love","heaven").find(text()); + Assert.assertEquals(res.size(),1); + Assert.assertEquals(res.get(0),"With a love that the winged seraphs of heaven"); + } + + @Test + public void expandedFinderTest() { + var res = expandedFinder("It was","kingdom").find(text()); + Assert.assertEquals(res.size(),3); + Assert.assertEquals(res.get(0),"It was many and many a year ago,"); + Assert.assertEquals(res.get(1),"In a kingdom by the sea,"); + Assert.assertEquals(res.get(2),"In this kingdom by the sea;"); + } + + + private String text(){ + return + "It was many and many a year ago,\n" + + "In a kingdom by the sea,\n" + + "That a maiden there lived whom you may know\n" + + "By the name of ANNABEL LEE;\n" + + "And this maiden she lived with no other thought\n" + + "Than to love and be loved by me.\n" + + "I was a child and she was a child,\n" + + "In this kingdom by the sea;\n" + + "But we loved with a love that was more than love-\n" + + "I and my Annabel Lee;\n" + + "With a love that the winged seraphs of heaven\n" + + "Coveted her and me."; + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9ffaa8525..ba6655c3d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,18 +1,18 @@ - circuit-breaker role-object saga - double-buffer - sharding - game-loop - + double-buffer + sharding + game-loop + combinator + - - - jitpack.io - https://jitpack.io - - + + + jitpack.io + https://jitpack.io + + @@ -387,7 +388,7 @@ jar-with-dependencies - ${project.artifactId} false @@ -422,8 +423,8 @@ - org.commonjava.maven.plugins