Skip to content

Commit 05e582c

Browse files
besokiluwatar
authored andcommitted
Pattern combinator (iluwatar#1105)
* init * add pattern
1 parent a9c3df7 commit 05e582c

File tree

9 files changed

+556
-27
lines changed

9 files changed

+556
-27
lines changed

combinator/README.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
layout: pattern
3+
title: Combinator
4+
folder: combinator
5+
permalink: /patterns/combinator/
6+
categories: Behavioral
7+
tags:
8+
- Functional
9+
- Reactive
10+
- Idiom
11+
---
12+
13+
## Also known as
14+
Composition pattern
15+
16+
## Intent
17+
The functional pattern representing a style of organizing libraries centered around the idea of combining functions.
18+
Putting it simply, there is some type T, some functions for constructing "primitive" values of type T,
19+
and some "combinators" which can combine values of type T in various ways to build up more complex values of type T.
20+
21+
22+
## Applicability
23+
Use the combinator pattern when:
24+
- You are able to create a more complex value from more plain values but having the same type(a combination of them)
25+
26+
## Real world examples
27+
- java.util.function.Function#compose
28+
- java.util.function.Function#andThen
29+
30+
## Credits
31+
- [Example for java](https://gtrefs.github.io/code/combinator-pattern/)
32+
- [Combinator pattern](https://wiki.haskell.org/Combinator_pattern)
33+
- [Combinatory logic](https://wiki.haskell.org/Combinatory_logic)

combinator/pom.xml

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
4+
The MIT License
5+
Copyright © 2014-2019 Ilkka Seppälä
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
25+
-->
26+
<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"
27+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
28+
<modelVersion>4.0.0</modelVersion>
29+
<parent>
30+
<groupId>com.iluwatar</groupId>
31+
<artifactId>java-design-patterns</artifactId>
32+
<version>1.23.0-SNAPSHOT</version>
33+
</parent>
34+
35+
<artifactId>combinator</artifactId>
36+
<dependencies>
37+
<dependency>
38+
<groupId>junit</groupId>
39+
<artifactId>junit</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
</dependencies>
43+
44+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* The MIT License
3+
* Copyright © 2014-2019 Ilkka Seppälä
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.combinator;
25+
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
30+
/**
31+
* The functional pattern representing a style of organizing libraries
32+
* centered around the idea of combining functions.
33+
* Putting it simply, there is some type T, some functions
34+
* for constructing "primitive" values of type T,
35+
* and some "combinators" which can combine values of type T
36+
* in various ways to build up more complex values of type T.
37+
* The class {@link Finder} defines a simple function {@link Finder#find(String)}
38+
* and connected functions
39+
* {@link Finder#or(Finder)},
40+
* {@link Finder#not(Finder)},
41+
* {@link Finder#and(Finder)}
42+
* Using them the became possible to get more complex functions {@link Finders}
43+
*/
44+
public class CombinatorApp {
45+
46+
/**
47+
* Logger.
48+
*/
49+
private static final Logger LOGGER = LoggerFactory.getLogger(CombinatorApp.class);
50+
51+
/**
52+
* main.
53+
* @param args args
54+
*/
55+
public static void main(String[] args) {
56+
var queriesOr = new String[]{"many", "Annabel"};
57+
var finder = Finders.expandedFinder(queriesOr);
58+
var res = finder.find(text());
59+
LOGGER.info("the result of expanded(or) query[{}] is {}", queriesOr, res);
60+
61+
var queriesAnd = new String[]{"Annabel", "my"};
62+
finder = Finders.specializedFinder(queriesAnd);
63+
res = finder.find(text());
64+
LOGGER.info("the result of specialized(and) query[{}] is {}", queriesAnd, res);
65+
66+
finder = Finders.advancedFinder("it was","kingdom","sea");
67+
res = finder.find(text());
68+
LOGGER.info("the result of advanced query is {}", res);
69+
70+
res = Finders.filteredFinder(" was ", "many", "child").find(text());
71+
LOGGER.info("the result of filtered query is {}", res);
72+
73+
74+
}
75+
76+
private static String text() {
77+
return
78+
"It was many and many a year ago,\n"
79+
+ "In a kingdom by the sea,\n"
80+
+ "That a maiden there lived whom you may know\n"
81+
+ "By the name of ANNABEL LEE;\n"
82+
+ "And this maiden she lived with no other thought\n"
83+
+ "Than to love and be loved by me.\n"
84+
+ "I was a child and she was a child,\n"
85+
+ "In this kingdom by the sea;\n"
86+
+ "But we loved with a love that was more than love-\n"
87+
+ "I and my Annabel Lee;\n"
88+
+ "With a love that the winged seraphs of heaven\n"
89+
+ "Coveted her and me.";
90+
}
91+
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* The MIT License
3+
* Copyright © 2014-2019 Ilkka Seppälä
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.combinator;
25+
26+
import java.util.List;
27+
import java.util.stream.Collectors;
28+
import java.util.stream.Stream;
29+
30+
/**
31+
* Functional interface to find lines in text.
32+
*/
33+
public interface Finder {
34+
35+
/**
36+
* The function to find lines in text.
37+
* @param text full tet
38+
* @return result of searching
39+
*/
40+
List<String> find(String text);
41+
42+
/**
43+
* Simple implementation of function {@link #find(String)}.
44+
* @param word for searching
45+
* @return this
46+
*/
47+
static Finder contains(String word) {
48+
return txt -> Stream.of(txt.split("\n"))
49+
.filter(line -> line.toLowerCase().contains(word.toLowerCase()))
50+
.collect(Collectors.toList());
51+
}
52+
53+
/**
54+
* combinator not.
55+
* @param notFinder finder to combine
56+
* @return new finder including previous finders
57+
*/
58+
default Finder not(Finder notFinder) {
59+
return txt -> {
60+
List<String> res = this.find(txt);
61+
res.removeAll(notFinder.find(txt));
62+
return res;
63+
};
64+
}
65+
66+
/**
67+
* combinator or.
68+
* @param orFinder finder to combine
69+
* @return new finder including previous finders
70+
*/
71+
default Finder or(Finder orFinder) {
72+
return txt -> {
73+
List<String> res = this.find(txt);
74+
res.addAll(orFinder.find(txt));
75+
return res;
76+
};
77+
}
78+
79+
/**
80+
* combinator or.
81+
* @param andFinder finder to combine
82+
* @return new finder including previous finders
83+
*/
84+
default Finder and(Finder andFinder) {
85+
return
86+
txt -> this
87+
.find(txt)
88+
.stream()
89+
.flatMap(line -> andFinder.find(line).stream())
90+
.collect(Collectors.toList());
91+
}
92+
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* The MIT License
3+
* Copyright © 2014-2019 Ilkka Seppälä
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.combinator;
25+
26+
import java.util.ArrayList;
27+
import java.util.stream.Collectors;
28+
import java.util.stream.Stream;
29+
30+
/**
31+
* Complex finders consisting of simple finder.
32+
*/
33+
public class Finders {
34+
private Finders() {
35+
}
36+
37+
38+
/**
39+
* Finder to find a complex query.
40+
* @param query to find
41+
* @param orQuery alternative to find
42+
* @param notQuery exclude from search
43+
* @return new finder
44+
*/
45+
public static Finder advancedFinder(String query, String orQuery, String notQuery) {
46+
return
47+
Finder.contains(query)
48+
.or(Finder.contains(orQuery))
49+
.not(Finder.contains(notQuery));
50+
}
51+
52+
/**
53+
* Filtered finder looking a query with excluded queries as well.
54+
* @param query to find
55+
* @param excludeQueries to exclude
56+
* @return new finder
57+
*/
58+
public static Finder filteredFinder(String query, String... excludeQueries) {
59+
var finder = Finder.contains(query);
60+
61+
for (String q : excludeQueries) {
62+
finder = finder.not(Finder.contains(q));
63+
}
64+
return finder;
65+
66+
}
67+
68+
/**
69+
* Specialized query. Every next query is looked in previous result.
70+
* @param queries array with queries
71+
* @return new finder
72+
*/
73+
public static Finder specializedFinder(String... queries) {
74+
var finder = identMult();
75+
76+
for (String query : queries) {
77+
finder = finder.and(Finder.contains(query));
78+
}
79+
return finder;
80+
}
81+
82+
/**
83+
* Expanded query. Looking for alternatives.
84+
* @param queries array with queries.
85+
* @return new finder
86+
*/
87+
public static Finder expandedFinder(String... queries) {
88+
var finder = identSum();
89+
90+
for (String query : queries) {
91+
finder = finder.or(Finder.contains(query));
92+
}
93+
return finder;
94+
}
95+
96+
private static Finder identMult() {
97+
return txt -> Stream.of(txt.split("\n")).collect(Collectors.toList());
98+
}
99+
100+
private static Finder identSum() {
101+
return txt -> new ArrayList<>();
102+
}
103+
}

0 commit comments

Comments
 (0)