Pattern combinator (#1105)

*  init

*  add pattern
This commit is contained in:
Boris 2019-11-26 06:03:04 +00:00 committed by Ilkka Seppälä
parent a9c3df78ee
commit 05e582ca3e
9 changed files with 556 additions and 27 deletions

33
combinator/README.md Normal file
View File

@ -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)

44
combinator/pom.xml Normal file
View File

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<!--
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.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.23.0-SNAPSHOT</version>
</parent>
<artifactId>combinator</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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.";
}
}

View File

@ -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<String> 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<String> 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<String> 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());
}
}

View File

@ -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<>();
}
}

View File

@ -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[]{});
}
}

View File

@ -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 ");
}
}

View File

@ -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.";
}
}

View File

@ -180,6 +180,7 @@
<module>double-buffer</module>
<module>sharding</module>
<module>game-loop</module>
<module>combinator</module>
</modules>
<repositories>