Skip to content

Pattern combinator #1105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions combinator/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 44 additions & 0 deletions combinator/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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.";
}

}
93 changes: 93 additions & 0 deletions combinator/src/main/java/com/iluwatar/combinator/Finder.java
Original file line number Diff line number Diff line change
@@ -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());
}

}
103 changes: 103 additions & 0 deletions combinator/src/main/java/com/iluwatar/combinator/Finders.java
Original file line number Diff line number Diff line change
@@ -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<>();
}
}
Loading